diff --git a/client.go b/client.go index b40149671..529383957 100644 --- a/client.go +++ b/client.go @@ -58,6 +58,9 @@ type OpenIDConnectClient interface { // public key used by the client to authenticate. GetJSONWebKeysURI() string + // GetJSONWebTokenClientAssertionIssuer returns the issuer of client assertions used to authenticate. + GetJSONWebTokenClientAssertionIssuer() string + // JWS [JWS] alg algorithm [JWA] that MUST be used for signing Request Objects sent to the OP. // All Request Objects from this Client MUST be rejected, if not signed with this algorithm. GetRequestObjectSigningAlgorithm() string @@ -94,6 +97,7 @@ type DefaultOpenIDConnectClient struct { *DefaultClient JSONWebKeysURI string `json:"jwks_uri"` JSONWebKeys *jose.JSONWebKeySet `json:"jwks"` + JSONWebTokenClientAssertionIssuer string `json:"jwt_client_assertion_issuer"` TokenEndpointAuthMethod string `json:"token_endpoint_auth_method"` RequestURIs []string `json:"request_uris"` RequestObjectSigningAlgorithm string `json:"request_object_signing_alg"` @@ -165,6 +169,13 @@ func (c *DefaultOpenIDConnectClient) GetJSONWebKeys() *jose.JSONWebKeySet { return c.JSONWebKeys } +func (c *DefaultOpenIDConnectClient) GetJSONWebTokenClientAssertionIssuer() string { + if c.JSONWebTokenClientAssertionIssuer == "" { + return c.ID + } + return c.JSONWebTokenClientAssertionIssuer +} + func (c *DefaultOpenIDConnectClient) GetTokenEndpointAuthSigningAlgorithm() string { if c.TokenEndpointAuthSigningAlgorithm == "" { return "RS256" diff --git a/client_authentication.go b/client_authentication.go index 251509199..2c4ceafb5 100644 --- a/client_authentication.go +++ b/client_authentication.go @@ -148,8 +148,8 @@ func (f *Fosite) DefaultClientAuthenticationStrategy(ctx context.Context, r *htt claims := token.Claims var jti string - if !claims.VerifyIssuer(clientID, true) { - return nil, errorsx.WithStack(ErrInvalidClient.WithHint("Claim 'iss' from 'client_assertion' must match the 'client_id' of the OAuth 2.0 Client.")) + if !claims.VerifyIssuer(client.(OpenIDConnectClient).GetJSONWebTokenClientAssertionIssuer(), true) { + return nil, errorsx.WithStack(ErrInvalidClient.WithHint("Claim 'iss' from 'client_assertion' must match the configured issuer for the OAuth 2.0 Client.")) } else if len(f.Config.GetTokenURLs(ctx)) == 0 { return nil, errorsx.WithStack(ErrMisconfiguration.WithHint("The authorization server's token endpoint URL has not been set.")) } else if sub, ok := claims["sub"].(string); !ok || sub != clientID { diff --git a/client_authentication_test.go b/client_authentication_test.go index aee4d6b59..11016cbed 100644 --- a/client_authentication_test.go +++ b/client_authentication_test.go @@ -465,6 +465,18 @@ func TestAuthenticateClient(t *testing.T) { }, rsaKey, "kid-foo")}, "client_assertion_type": []string{at}}, r: new(http.Request), }, + { + d: "should pass with proper assertion when JWT client assertion issuer is set", + client: &DefaultOpenIDConnectClient{DefaultClient: &DefaultClient{ID: "bar", Secret: barSecret}, JSONWebKeysURI: ts.URL, JSONWebTokenClientAssertionIssuer: "baz", TokenEndpointAuthMethod: "private_key_jwt"}, + form: url.Values{"client_id": []string{"bar"}, "client_assertion": {mustGenerateRSAAssertion(t, jwt.MapClaims{ + "sub": "bar", + "exp": time.Now().Add(time.Hour).Unix(), + "iss": "baz", + "jti": "12345", + "aud": "token-url", + }, rsaKey, "kid-foo")}, "client_assertion_type": []string{at}}, + r: new(http.Request), + }, { d: "should fail because client_assertion sub does not match client", client: &DefaultOpenIDConnectClient{DefaultClient: &DefaultClient{ID: "bar", Secret: barSecret}, JSONWebKeys: rsaJwks, TokenEndpointAuthMethod: "private_key_jwt"}, @@ -491,6 +503,19 @@ func TestAuthenticateClient(t *testing.T) { r: new(http.Request), expectErr: ErrInvalidClient, }, + { + d: "should fail because client_assertion iss does not match the configured issuer", + client: &DefaultOpenIDConnectClient{DefaultClient: &DefaultClient{ID: "bar", Secret: barSecret}, JSONWebKeys: rsaJwks, JSONWebTokenClientAssertionIssuer: "baz", TokenEndpointAuthMethod: "private_key_jwt"}, + form: url.Values{"client_id": []string{"bar"}, "client_assertion": {mustGenerateRSAAssertion(t, jwt.MapClaims{ + "sub": "bar", + "exp": time.Now().Add(time.Hour).Unix(), + "iss": "not-baz", + "jti": "12345", + "aud": "token-url", + }, rsaKey, "kid-foo")}, "client_assertion_type": []string{at}}, + r: new(http.Request), + expectErr: ErrInvalidClient, + }, { d: "should fail because client_assertion jti is not set", client: &DefaultOpenIDConnectClient{DefaultClient: &DefaultClient{ID: "bar", Secret: barSecret}, JSONWebKeys: rsaJwks, TokenEndpointAuthMethod: "private_key_jwt"},