Skip to content
5 changes: 5 additions & 0 deletions .schema/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -900,6 +900,11 @@
"$ref": "#/definitions/duration"
}
]
},
"omit_assertion_audience": {
"type": "boolean",
"description": "Configures whether the audience from the assertion JWT in the jwt-bearer grant type is omitted from the resulting access token. When set to `true`, the audience values from the inbound assertion JWT are not granted in the access token. Defaults to `false` for backwards compatibility.",
"default": false
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions driver/config/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ const (
KeyOAuth2GrantJWTIDOptional = "oauth2.grant.jwt.jti_optional"
KeyOAuth2GrantJWTIssuedDateOptional = "oauth2.grant.jwt.iat_optional"
KeyOAuth2GrantJWTMaxDuration = "oauth2.grant.jwt.max_ttl"
KeyOAuth2GrantJWTOmitAssertionAudience = "oauth2.grant.jwt.omit_assertion_audience"
KeyRefreshTokenHook = "oauth2.refresh_token_hook" // #nosec G101
KeyTokenHook = "oauth2.token_hook" // #nosec G101
KeyDevelopmentMode = "dev"
Expand Down Expand Up @@ -740,6 +741,10 @@ func (p *DefaultProvider) GetGrantTypeJWTBearerIssuedDateOptional(ctx context.Co
return p.getProvider(ctx).Bool(KeyOAuth2GrantJWTIssuedDateOptional)
}

func (p *DefaultProvider) GetGrantTypeJWTBearerOmitAssertionAudience(ctx context.Context) bool {
return p.getProvider(ctx).Bool(KeyOAuth2GrantJWTOmitAssertionAudience)
}

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, false, p.GetGrantTypeJWTBearerOmitAssertionAudience(ctx), "should default to false 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, KeyOAuth2GrantJWTOmitAssertionAudience, true)

// 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, true, p2.GetGrantTypeJWTBearerOmitAssertionAudience(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
}

// GrantTypeJWTBearerOmitAssertionAudienceProvider returns the provider for configuring whether the audience from the
// assertion JWT is omitted from the resulting access token in the jwt-bearer grant type.
type GrantTypeJWTBearerOmitAssertionAudienceProvider interface {
// GetGrantTypeJWTBearerOmitAssertionAudience returns whether the audience from the assertion JWT should be
// omitted from the resulting access token. Defaults to false for backwards compatibility.
GetGrantTypeJWTBearerOmitAssertionAudience(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
88 changes: 49 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)
_ GrantTypeJWTBearerOmitAssertionAudienceProvider = (*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

// GrantTypeJWTBearerOmitAssertionAudience indicates whether the audience from the assertion JWT should be
// omitted from the resulting access token. Defaults to false for backwards compatibility.
GrantTypeJWTBearerOmitAssertionAudience 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,11 @@ func (c *Config) GetGrantTypeJWTBearerIDOptional(ctx context.Context) bool {
return c.GrantTypeJWTBearerIDOptional
}

// GetGrantTypeJWTBearerOmitAssertionAudience returns GrantTypeJWTBearerOmitAssertionAudience.
func (c *Config) GetGrantTypeJWTBearerOmitAssertionAudience(ctx context.Context) bool {
return c.GrantTypeJWTBearerOmitAssertionAudience
}

// 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
GrantTypeJWTBearerOmitAssertionAudienceProvider
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.GrantTypeJWTBearerOmitAssertionAudienceProvider
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.GetGrantTypeJWTBearerOmitAssertionAudience(ctx) {
for _, audience := range claims.Audience {
request.GrantAudience(audience)
}
}

session, err := c.getSessionFromRequest(request)
Expand Down
56 changes: 56 additions & 0 deletions fosite/handler/rfc7523/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,62 @@ 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()
s.handler.Config.(*fosite.Config).GrantTypeJWTBearerOmitAssertionAudience = true

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 @@ -900,6 +900,11 @@
"$ref": "#/definitions/duration"
}
]
},
"omit_assertion_audience": {
"type": "boolean",
"description": "Configures whether the audience from the assertion JWT in the jwt-bearer grant type is omitted from the resulting access token. When set to `true`, the audience values from the inbound assertion JWT are not granted in the access token. Defaults to `false` for backwards compatibility.",
"default": false
}
}
}
Expand Down
Loading