Skip to content
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
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
16 changes: 14 additions & 2 deletions fosite/handler/rfc7523/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ import (
// #nosec:gosec G101 - False Positive
const grantTypeJWTBearer = "urn:ietf:params:oauth:grant-type:jwt-bearer"

// omitAssertionAudienceProvider is an optional interface that can be implemented
// by the Config to control whether the audience from the assertion JWT is omitted.
type omitAssertionAudienceProvider interface {
GetGrantTypeJWTBearerOmitAssertionAudience(ctx context.Context) bool
}

var _ fosite.TokenEndpointHandler = (*Handler)(nil)

type Handler struct {
Expand Down Expand Up @@ -103,8 +109,14 @@ func (c *Handler) HandleTokenEndpointRequest(ctx context.Context, request fosite
request.GrantScope(scope)
}

for _, audience := range claims.Audience {
request.GrantAudience(audience)
omitAudience := false
if p, ok := c.Config.(omitAssertionAudienceProvider); ok {
omitAudience = p.GetGrantTypeJWTBearerOmitAssertionAudience(ctx)
}
if !omitAudience {
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 GrantTypeJWTBearerOmitAssertionAudience is true",
)
}

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