Skip to content
Draft
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
5 changes: 5 additions & 0 deletions .schema/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -894,6 +894,11 @@
"$ref": "#/definitions/duration"
}
]
},
"copy_assertion_audience": {
"type": "boolean",
"description": "Configures whether the audience from the assertion JWT in the jwt-bearer grant type is copied into the resulting access token. When set to `true` (the default), the audience values from the inbound assertion JWT are granted in the access token. Set to `false` to prevent the assertion audience from being copied into the access token.",
"default": true
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions driver/config/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ const (
KeyOAuth2GrantJWTIDOptional = "oauth2.grant.jwt.jti_optional"
KeyOAuth2GrantJWTIssuedDateOptional = "oauth2.grant.jwt.iat_optional"
KeyOAuth2GrantJWTMaxDuration = "oauth2.grant.jwt.max_ttl"
KeyOAuth2GrantJWTCopyAssertionAudience = "oauth2.grant.jwt.copy_assertion_audience"
KeyRefreshTokenHook = "oauth2.refresh_token_hook" // #nosec G101
KeyTokenHook = "oauth2.token_hook" // #nosec G101
KeyDevelopmentMode = "dev"
Expand Down Expand Up @@ -724,6 +725,10 @@ func (p *DefaultProvider) GetGrantTypeJWTBearerIssuedDateOptional(ctx context.Co
return p.getProvider(ctx).Bool(KeyOAuth2GrantJWTIssuedDateOptional)
}

func (p *DefaultProvider) GetGrantTypeJWTBearerCopyAssertionAudience(ctx context.Context) bool {
return p.getProvider(ctx).BoolF(KeyOAuth2GrantJWTCopyAssertionAudience, true)
}

func (p *DefaultProvider) GetJWTMaxDuration(ctx context.Context) time.Duration {
return p.getProvider(ctx).DurationF(KeyOAuth2GrantJWTMaxDuration, time.Hour*24*30)
}
Expand Down
3 changes: 3 additions & 0 deletions driver/config/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -502,18 +502,21 @@ func TestJWTBearer(t *testing.T) {
assert.Equal(t, 1.0, p.GetJWTMaxDuration(ctx).Hours())
assert.Equal(t, false, p.GetGrantTypeJWTBearerIssuedDateOptional(ctx))
assert.Equal(t, false, p.GetGrantTypeJWTBearerIDOptional(ctx))
assert.Equal(t, true, p.GetGrantTypeJWTBearerCopyAssertionAudience(ctx), "should default to true when not set")

p2 := MustNew(t, l)

// p2.MustSet(ctx, KeyOAuth2GrantJWTClientAuthOptional, true)
p2.MustSet(ctx, KeyOAuth2GrantJWTMaxDuration, "24h")
p2.MustSet(ctx, KeyOAuth2GrantJWTIssuedDateOptional, true)
p2.MustSet(ctx, KeyOAuth2GrantJWTIDOptional, true)
p2.MustSet(ctx, KeyOAuth2GrantJWTCopyAssertionAudience, false)

// assert.Equal(t, true, p2.GetGrantTypeJWTBearerCanSkipClientAuth(ctx))
assert.Equal(t, 24.0, p2.GetJWTMaxDuration(ctx).Hours())
assert.Equal(t, true, p2.GetGrantTypeJWTBearerIssuedDateOptional(ctx))
assert.Equal(t, true, p2.GetGrantTypeJWTBearerIDOptional(ctx))
assert.Equal(t, false, p2.GetGrantTypeJWTBearerCopyAssertionAudience(ctx))
}

func TestJWTScopeClaimStrategy(t *testing.T) {
Expand Down
8 changes: 8 additions & 0 deletions fosite/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,14 @@ type GrantTypeJWTBearerIssuedDateOptionalProvider interface {
GetGrantTypeJWTBearerIssuedDateOptional(ctx context.Context) bool
}

// GrantTypeJWTBearerCopyAssertionAudienceProvider returns the provider for configuring whether the audience from the
// assertion JWT is copied into the resulting access token in the jwt-bearer grant type.
type GrantTypeJWTBearerCopyAssertionAudienceProvider interface {
// GetGrantTypeJWTBearerCopyAssertionAudience returns whether the audience from the assertion JWT should be
// copied into the resulting access token. Defaults to true for backwards compatibility.
GetGrantTypeJWTBearerCopyAssertionAudience(ctx context.Context) bool
}

// GetJWTMaxDurationProvider returns the provider for configuring the JWT max duration.
type GetJWTMaxDurationProvider interface {
// GetJWTMaxDuration returns the JWT max duration.
Expand Down
92 changes: 53 additions & 39 deletions fosite/config_default.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,45 +26,46 @@ const (
)

var (
_ AuthorizeCodeLifespanProvider = (*Config)(nil)
_ RefreshTokenLifespanProvider = (*Config)(nil)
_ AccessTokenLifespanProvider = (*Config)(nil)
_ ScopeStrategyProvider = (*Config)(nil)
_ AudienceStrategyProvider = (*Config)(nil)
_ RedirectSecureCheckerProvider = (*Config)(nil)
_ RefreshTokenScopesProvider = (*Config)(nil)
_ DisableRefreshTokenValidationProvider = (*Config)(nil)
_ AccessTokenIssuerProvider = (*Config)(nil)
_ JWTScopeFieldProvider = (*Config)(nil)
_ AllowedPromptsProvider = (*Config)(nil)
_ OmitRedirectScopeParamProvider = (*Config)(nil)
_ MinParameterEntropyProvider = (*Config)(nil)
_ SanitationAllowedProvider = (*Config)(nil)
_ EnforcePKCEForPublicClientsProvider = (*Config)(nil)
_ EnablePKCEPlainChallengeMethodProvider = (*Config)(nil)
_ EnforcePKCEProvider = (*Config)(nil)
_ GrantTypeJWTBearerCanSkipClientAuthProvider = (*Config)(nil)
_ GrantTypeJWTBearerIDOptionalProvider = (*Config)(nil)
_ GrantTypeJWTBearerIssuedDateOptionalProvider = (*Config)(nil)
_ GetJWTMaxDurationProvider = (*Config)(nil)
_ IDTokenLifespanProvider = (*Config)(nil)
_ IDTokenIssuerProvider = (*Config)(nil)
_ JWKSFetcherStrategyProvider = (*Config)(nil)
_ ClientAuthenticationStrategyProvider = (*Config)(nil)
_ SendDebugMessagesToClientsProvider = (*Config)(nil)
_ ResponseModeHandlerExtensionProvider = (*Config)(nil)
_ MessageCatalogProvider = (*Config)(nil)
_ FormPostHTMLTemplateProvider = (*Config)(nil)
_ TokenURLProvider = (*Config)(nil)
_ GetSecretsHashingProvider = (*Config)(nil)
_ HTTPClientProvider = (*Config)(nil)
_ HMACHashingProvider = (*Config)(nil)
_ AuthorizeEndpointHandlersProvider = (*Config)(nil)
_ TokenEndpointHandlersProvider = (*Config)(nil)
_ TokenIntrospectionHandlersProvider = (*Config)(nil)
_ RevocationHandlersProvider = (*Config)(nil)
_ PushedAuthorizeRequestHandlersProvider = (*Config)(nil)
_ PushedAuthorizeRequestConfigProvider = (*Config)(nil)
_ AuthorizeCodeLifespanProvider = (*Config)(nil)
_ RefreshTokenLifespanProvider = (*Config)(nil)
_ AccessTokenLifespanProvider = (*Config)(nil)
_ ScopeStrategyProvider = (*Config)(nil)
_ AudienceStrategyProvider = (*Config)(nil)
_ RedirectSecureCheckerProvider = (*Config)(nil)
_ RefreshTokenScopesProvider = (*Config)(nil)
_ DisableRefreshTokenValidationProvider = (*Config)(nil)
_ AccessTokenIssuerProvider = (*Config)(nil)
_ JWTScopeFieldProvider = (*Config)(nil)
_ AllowedPromptsProvider = (*Config)(nil)
_ OmitRedirectScopeParamProvider = (*Config)(nil)
_ MinParameterEntropyProvider = (*Config)(nil)
_ SanitationAllowedProvider = (*Config)(nil)
_ EnforcePKCEForPublicClientsProvider = (*Config)(nil)
_ EnablePKCEPlainChallengeMethodProvider = (*Config)(nil)
_ EnforcePKCEProvider = (*Config)(nil)
_ GrantTypeJWTBearerCanSkipClientAuthProvider = (*Config)(nil)
_ GrantTypeJWTBearerIDOptionalProvider = (*Config)(nil)
_ GrantTypeJWTBearerIssuedDateOptionalProvider = (*Config)(nil)
_ GrantTypeJWTBearerCopyAssertionAudienceProvider = (*Config)(nil)
_ GetJWTMaxDurationProvider = (*Config)(nil)
_ IDTokenLifespanProvider = (*Config)(nil)
_ IDTokenIssuerProvider = (*Config)(nil)
_ JWKSFetcherStrategyProvider = (*Config)(nil)
_ ClientAuthenticationStrategyProvider = (*Config)(nil)
_ SendDebugMessagesToClientsProvider = (*Config)(nil)
_ ResponseModeHandlerExtensionProvider = (*Config)(nil)
_ MessageCatalogProvider = (*Config)(nil)
_ FormPostHTMLTemplateProvider = (*Config)(nil)
_ TokenURLProvider = (*Config)(nil)
_ GetSecretsHashingProvider = (*Config)(nil)
_ HTTPClientProvider = (*Config)(nil)
_ HMACHashingProvider = (*Config)(nil)
_ AuthorizeEndpointHandlersProvider = (*Config)(nil)
_ TokenEndpointHandlersProvider = (*Config)(nil)
_ TokenIntrospectionHandlersProvider = (*Config)(nil)
_ RevocationHandlersProvider = (*Config)(nil)
_ PushedAuthorizeRequestHandlersProvider = (*Config)(nil)
_ PushedAuthorizeRequestConfigProvider = (*Config)(nil)
)

type Config struct {
Expand Down Expand Up @@ -160,6 +161,10 @@ type Config struct {
// GrantTypeJWTBearerIssuedDateOptional indicates, if "iat" (issued at) claim required or not in JWT.
GrantTypeJWTBearerIssuedDateOptional bool

// GrantTypeJWTBearerCopyAssertionAudience indicates whether the audience from the assertion JWT should be
// copied into the resulting access token. Defaults to true for backwards compatibility.
GrantTypeJWTBearerCopyAssertionAudience *bool

// GrantTypeJWTBearerMaxDuration sets the maximum time after JWT issued date, during which the JWT is considered valid.
GrantTypeJWTBearerMaxDuration time.Duration

Expand Down Expand Up @@ -323,6 +328,15 @@ func (c *Config) GetGrantTypeJWTBearerIDOptional(ctx context.Context) bool {
return c.GrantTypeJWTBearerIDOptional
}

// GetGrantTypeJWTBearerCopyAssertionAudience returns GrantTypeJWTBearerCopyAssertionAudience.
// Defaults to true if not explicitly set, for backwards compatibility.
func (c *Config) GetGrantTypeJWTBearerCopyAssertionAudience(ctx context.Context) bool {
if c.GrantTypeJWTBearerCopyAssertionAudience == nil {
return true
}
return *c.GrantTypeJWTBearerCopyAssertionAudience
}

// GetGrantTypeJWTBearerCanSkipClientAuth returns the GrantTypeJWTBearerCanSkipClientAuth field.
func (c *Config) GetGrantTypeJWTBearerCanSkipClientAuth(ctx context.Context) bool {
return c.GrantTypeJWTBearerCanSkipClientAuth
Expand Down
1 change: 1 addition & 0 deletions fosite/fosite.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ type Configurator interface {
GrantTypeJWTBearerCanSkipClientAuthProvider
GrantTypeJWTBearerIDOptionalProvider
GrantTypeJWTBearerIssuedDateOptionalProvider
GrantTypeJWTBearerCopyAssertionAudienceProvider
GetJWTMaxDurationProvider
AudienceStrategyProvider
ScopeStrategyProvider
Expand Down
7 changes: 5 additions & 2 deletions fosite/handler/rfc7523/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type Handler struct {
fosite.GrantTypeJWTBearerCanSkipClientAuthProvider
fosite.GrantTypeJWTBearerIDOptionalProvider
fosite.GrantTypeJWTBearerIssuedDateOptionalProvider
fosite.GrantTypeJWTBearerCopyAssertionAudienceProvider
fosite.GetJWTMaxDurationProvider
fosite.AudienceStrategyProvider
fosite.ScopeStrategyProvider
Expand Down Expand Up @@ -103,8 +104,10 @@ func (c *Handler) HandleTokenEndpointRequest(ctx context.Context, request fosite
request.GrantScope(scope)
}

for _, audience := range claims.Audience {
request.GrantAudience(audience)
if c.Config.GetGrantTypeJWTBearerCopyAssertionAudience(ctx) {
for _, audience := range claims.Audience {
request.GrantAudience(audience)
}
}

session, err := c.getSessionFromRequest(request)
Expand Down
57 changes: 57 additions & 0 deletions fosite/handler/rfc7523/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,63 @@ func (s *AuthorizeJWTGrantRequestHandlerTestSuite) TestValidAssertion() {
s.NoError(err, "no error expected, because assertion must be valid")
}

func (s *AuthorizeJWTGrantRequestHandlerTestSuite) TestValidAssertionCopiesAudienceByDefault() {
// arrange
ctx := context.Background()
s.accessRequest.GrantTypes = []string{grantTypeJWTBearer}
keyID := "my_key"
pubKey := s.createJWK(s.privateKey.Public(), keyID)
cl := s.createStandardClaim()

s.accessRequest.Form.Add("assertion", s.createTestAssertion(cl, keyID))
s.accessRequest.RequestedScope = []string{"valid_scope"}
s.mockStoreProvider.EXPECT().RFC7523KeyStorage().Return(s.mockStore).Times(4)
s.mockStore.EXPECT().GetPublicKey(ctx, cl.Issuer, cl.Subject, keyID).Return(&pubKey, nil)
s.mockStore.EXPECT().GetPublicKeyScopes(ctx, cl.Issuer, cl.Subject, keyID).Return([]string{"valid_scope", "openid"}, nil)
s.mockStore.EXPECT().IsJWTUsed(ctx, cl.ID).Return(false, nil)
s.mockStore.EXPECT().MarkJWTUsedForTime(ctx, cl.ID, cl.Expiry.Time()).Return(nil)

// act
err := s.handler.HandleTokenEndpointRequest(ctx, s.accessRequest)

// assert
s.NoError(err, "no error expected, because assertion must be valid")
s.ElementsMatch(
[]string{"https://www.example.com/token", "leela", "fry"},
s.accessRequest.GetGrantedAudience(),
"audience from assertion JWT should be copied to the access request by default",
)
}

func (s *AuthorizeJWTGrantRequestHandlerTestSuite) TestValidAssertionDoesNotCopyAudienceWhenDisabled() {
// arrange
ctx := context.Background()
s.accessRequest.GrantTypes = []string{grantTypeJWTBearer}
keyID := "my_key"
pubKey := s.createJWK(s.privateKey.Public(), keyID)
cl := s.createStandardClaim()
copyAudience := false
s.handler.Config.(*fosite.Config).GrantTypeJWTBearerCopyAssertionAudience = &copyAudience

s.accessRequest.Form.Add("assertion", s.createTestAssertion(cl, keyID))
s.accessRequest.RequestedScope = []string{"valid_scope"}
s.mockStoreProvider.EXPECT().RFC7523KeyStorage().Return(s.mockStore).Times(4)
s.mockStore.EXPECT().GetPublicKey(ctx, cl.Issuer, cl.Subject, keyID).Return(&pubKey, nil)
s.mockStore.EXPECT().GetPublicKeyScopes(ctx, cl.Issuer, cl.Subject, keyID).Return([]string{"valid_scope", "openid"}, nil)
s.mockStore.EXPECT().IsJWTUsed(ctx, cl.ID).Return(false, nil)
s.mockStore.EXPECT().MarkJWTUsedForTime(ctx, cl.ID, cl.Expiry.Time()).Return(nil)

// act
err := s.handler.HandleTokenEndpointRequest(ctx, s.accessRequest)

// assert
s.NoError(err, "no error expected, because assertion must be valid")
s.Empty(
s.accessRequest.GetGrantedAudience(),
"audience from assertion JWT should NOT be copied when CopyAssertionAudience is false",
)
}

func (s *AuthorizeJWTGrantRequestHandlerTestSuite) TestAssertionIsValidWhenNoScopesPassed() {
// arrange
ctx := context.Background()
Expand Down
5 changes: 5 additions & 0 deletions spec/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -894,6 +894,11 @@
"$ref": "#/definitions/duration"
}
]
},
"copy_assertion_audience": {
"type": "boolean",
"description": "Configures whether the audience from the assertion JWT in the jwt-bearer grant type is copied into the resulting access token. When set to `true` (the default), the audience values from the inbound assertion JWT are granted in the access token. Set to `false` to prevent the assertion audience from being copied into the access token.",
"default": true
}
}
}
Expand Down
Loading