Skip to content

feat: configure client assertion issuer #847

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"`
Expand Down Expand Up @@ -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"
Expand Down
4 changes: 2 additions & 2 deletions client_authentication.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
25 changes: 25 additions & 0 deletions client_authentication_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"},
Expand All @@ -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"},
Expand Down
Loading