diff --git a/compose/compose_strategy.go b/compose/compose_strategy.go index 733d46774..6e7c845a8 100644 --- a/compose/compose_strategy.go +++ b/compose/compose_strategy.go @@ -22,6 +22,7 @@ type CommonStrategy struct { type HMACSHAStrategyConfigurator interface { fosite.AccessTokenLifespanProvider fosite.RefreshTokenLifespanProvider + fosite.TokenPrefixProvider fosite.AuthorizeCodeLifespanProvider fosite.TokenEntropyProvider fosite.GlobalSecretProvider diff --git a/config.go b/config.go index 983a35940..e1f1cb293 100644 --- a/config.go +++ b/config.go @@ -34,6 +34,12 @@ type AccessTokenLifespanProvider interface { GetAccessTokenLifespan(ctx context.Context) time.Duration } +// TokenPrefixProvider returns the provider for configuring the token prefix. +type TokenPrefixProvider interface { + // GetAccessTokenLifespan returns the access token lifespan. + GetTokenPrefixProvider(ctx context.Context) string +} + // IDTokenLifespanProvider returns the provider for configuring the ID token lifespan. type IDTokenLifespanProvider interface { // GetIDTokenLifespan returns the ID token lifespan. diff --git a/config_default.go b/config_default.go index df6fa2a50..c0566964d 100644 --- a/config_default.go +++ b/config_default.go @@ -26,6 +26,7 @@ var ( _ AuthorizeCodeLifespanProvider = (*Config)(nil) _ RefreshTokenLifespanProvider = (*Config)(nil) _ AccessTokenLifespanProvider = (*Config)(nil) + _ TokenPrefixProvider = (*Config)(nil) _ ScopeStrategyProvider = (*Config)(nil) _ AudienceStrategyProvider = (*Config)(nil) _ RedirectSecureCheckerProvider = (*Config)(nil) @@ -72,6 +73,9 @@ type Config struct { // refresh tokens that never expire. RefreshTokenLifespan time.Duration + // TokenPrefix sets the prefix for hmac tokens to help identify tokens. Defaults to "ory" + TokenPrefix string + // AuthorizeCodeLifespan sets how long an authorize code is going to be valid. Defaults to fifteen minutes. AuthorizeCodeLifespan time.Duration @@ -328,6 +332,13 @@ func (c *Config) GetAccessTokenIssuer(ctx context.Context) string { return c.AccessTokenIssuer } +func (c *Config) GetTokenPrefixProvider(_ context.Context) string { + if c.TokenPrefix == "" { + c.TokenPrefix = "ory" + } + return c.TokenPrefix +} + func (c *Config) GetJWTScopeField(ctx context.Context) jwt.JWTScopeFieldEnum { return c.JWTScopeClaimKey } diff --git a/fosite.go b/fosite.go index 518319c60..d36fcb6f3 100644 --- a/fosite.go +++ b/fosite.go @@ -106,6 +106,7 @@ type Configurator interface { RefreshTokenScopesProvider AccessTokenLifespanProvider RefreshTokenLifespanProvider + TokenPrefixProvider AuthorizeCodeLifespanProvider TokenEntropyProvider RotatedGlobalSecretsProvider diff --git a/handler/oauth2/flow_authorize_code_auth.go b/handler/oauth2/flow_authorize_code_auth.go index f038647d6..0372ba901 100644 --- a/handler/oauth2/flow_authorize_code_auth.go +++ b/handler/oauth2/flow_authorize_code_auth.go @@ -29,6 +29,7 @@ type AuthorizeExplicitGrantHandler struct { fosite.AuthorizeCodeLifespanProvider fosite.AccessTokenLifespanProvider fosite.RefreshTokenLifespanProvider + fosite.TokenPrefixProvider fosite.ScopeStrategyProvider fosite.AudienceStrategyProvider fosite.RedirectSecureCheckerProvider diff --git a/handler/oauth2/flow_authorize_implicit.go b/handler/oauth2/flow_authorize_implicit.go index 91f90d498..44b172018 100644 --- a/handler/oauth2/flow_authorize_implicit.go +++ b/handler/oauth2/flow_authorize_implicit.go @@ -25,6 +25,7 @@ type AuthorizeImplicitGrantTypeHandler struct { Config interface { fosite.AccessTokenLifespanProvider + fosite.TokenPrefixProvider fosite.ScopeStrategyProvider fosite.AudienceStrategyProvider } diff --git a/handler/oauth2/flow_client_credentials.go b/handler/oauth2/flow_client_credentials.go index 8bc6ec9a5..6b6740939 100644 --- a/handler/oauth2/flow_client_credentials.go +++ b/handler/oauth2/flow_client_credentials.go @@ -20,6 +20,7 @@ type ClientCredentialsGrantHandler struct { fosite.ScopeStrategyProvider fosite.AudienceStrategyProvider fosite.AccessTokenLifespanProvider + fosite.TokenPrefixProvider } } diff --git a/handler/oauth2/flow_refresh.go b/handler/oauth2/flow_refresh.go index 77a5f91e5..47cf513b2 100644 --- a/handler/oauth2/flow_refresh.go +++ b/handler/oauth2/flow_refresh.go @@ -26,6 +26,7 @@ type RefreshTokenGrantHandler struct { Config interface { fosite.AccessTokenLifespanProvider fosite.RefreshTokenLifespanProvider + fosite.TokenPrefixProvider fosite.ScopeStrategyProvider fosite.AudienceStrategyProvider fosite.RefreshTokenScopesProvider diff --git a/handler/oauth2/flow_resource_owner.go b/handler/oauth2/flow_resource_owner.go index 4a15d87a3..77b503aed 100644 --- a/handler/oauth2/flow_resource_owner.go +++ b/handler/oauth2/flow_resource_owner.go @@ -30,6 +30,7 @@ type ResourceOwnerPasswordCredentialsGrantHandler struct { fosite.RefreshTokenScopesProvider fosite.RefreshTokenLifespanProvider fosite.AccessTokenLifespanProvider + fosite.TokenPrefixProvider } } diff --git a/handler/oauth2/helper.go b/handler/oauth2/helper.go index 990bc0e4e..b0385864f 100644 --- a/handler/oauth2/helper.go +++ b/handler/oauth2/helper.go @@ -13,6 +13,7 @@ import ( type HandleHelperConfigProvider interface { fosite.AccessTokenLifespanProvider fosite.RefreshTokenLifespanProvider + fosite.TokenPrefixProvider } type HandleHelper struct { diff --git a/handler/oauth2/strategy_hmacsha.go b/handler/oauth2/strategy_hmacsha.go index fef520792..38d0eb5cc 100644 --- a/handler/oauth2/strategy_hmacsha.go +++ b/handler/oauth2/strategy_hmacsha.go @@ -20,6 +20,7 @@ type HMACSHAStrategy struct { Config interface { fosite.AccessTokenLifespanProvider fosite.RefreshTokenLifespanProvider + fosite.TokenPrefixProvider fosite.AuthorizeCodeLifespanProvider } prefix *string @@ -35,9 +36,9 @@ func (h *HMACSHAStrategy) AuthorizeCodeSignature(ctx context.Context, token stri return h.Enigma.Signature(token) } -func (h *HMACSHAStrategy) getPrefix(part string) string { +func (h *HMACSHAStrategy) getPrefix(ctx context.Context, part string) string { if h.prefix == nil { - prefix := "ory_%s_" + prefix := h.Config.GetTokenPrefixProvider(ctx) + "_%s_" h.prefix = &prefix } else if len(*h.prefix) == 0 { return "" @@ -46,12 +47,17 @@ func (h *HMACSHAStrategy) getPrefix(part string) string { return fmt.Sprintf(*h.prefix, part) } -func (h *HMACSHAStrategy) trimPrefix(token, part string) string { - return strings.TrimPrefix(token, h.getPrefix(part)) +func (h *HMACSHAStrategy) trimPrefix(ctx context.Context, token, part string) string { + // Support natural migration from ory prefix to custom prefix + legacyPrefix := fmt.Sprintf("ory_%s_", part) + return strings.TrimPrefix( + strings.TrimPrefix(token, legacyPrefix), + h.getPrefix(ctx, part), + ) } -func (h *HMACSHAStrategy) setPrefix(token, part string) string { - return h.getPrefix(part) + token +func (h *HMACSHAStrategy) setPrefix(ctx context.Context, token, part string) string { + return h.getPrefix(ctx, part) + token } func (h *HMACSHAStrategy) GenerateAccessToken(ctx context.Context, _ fosite.Requester) (token string, signature string, err error) { @@ -60,7 +66,7 @@ func (h *HMACSHAStrategy) GenerateAccessToken(ctx context.Context, _ fosite.Requ return "", "", err } - return h.setPrefix(token, "at"), sig, nil + return h.setPrefix(ctx, token, "at"), sig, nil } func (h *HMACSHAStrategy) ValidateAccessToken(ctx context.Context, r fosite.Requester, token string) (err error) { @@ -73,7 +79,7 @@ func (h *HMACSHAStrategy) ValidateAccessToken(ctx context.Context, r fosite.Requ return errorsx.WithStack(fosite.ErrTokenExpired.WithHintf("Access token expired at '%s'.", exp)) } - return h.Enigma.Validate(ctx, h.trimPrefix(token, "at")) + return h.Enigma.Validate(ctx, h.trimPrefix(ctx, token, "at")) } func (h *HMACSHAStrategy) GenerateRefreshToken(ctx context.Context, _ fosite.Requester) (token string, signature string, err error) { @@ -82,21 +88,21 @@ func (h *HMACSHAStrategy) GenerateRefreshToken(ctx context.Context, _ fosite.Req return "", "", err } - return h.setPrefix(token, "rt"), sig, nil + return h.setPrefix(ctx, token, "rt"), sig, nil } func (h *HMACSHAStrategy) ValidateRefreshToken(ctx context.Context, r fosite.Requester, token string) (err error) { var exp = r.GetSession().GetExpiresAt(fosite.RefreshToken) if exp.IsZero() { // Unlimited lifetime - return h.Enigma.Validate(ctx, h.trimPrefix(token, "rt")) + return h.Enigma.Validate(ctx, h.trimPrefix(ctx, token, "rt")) } if !exp.IsZero() && exp.Before(time.Now().UTC()) { return errorsx.WithStack(fosite.ErrTokenExpired.WithHintf("Refresh token expired at '%s'.", exp)) } - return h.Enigma.Validate(ctx, h.trimPrefix(token, "rt")) + return h.Enigma.Validate(ctx, h.trimPrefix(ctx, token, "rt")) } func (h *HMACSHAStrategy) GenerateAuthorizeCode(ctx context.Context, _ fosite.Requester) (token string, signature string, err error) { @@ -105,7 +111,7 @@ func (h *HMACSHAStrategy) GenerateAuthorizeCode(ctx context.Context, _ fosite.Re return "", "", err } - return h.setPrefix(token, "ac"), sig, nil + return h.setPrefix(ctx, token, "ac"), sig, nil } func (h *HMACSHAStrategy) ValidateAuthorizeCode(ctx context.Context, r fosite.Requester, token string) (err error) { @@ -118,5 +124,5 @@ func (h *HMACSHAStrategy) ValidateAuthorizeCode(ctx context.Context, r fosite.Re return errorsx.WithStack(fosite.ErrTokenExpired.WithHintf("Authorize code expired at '%s'.", exp)) } - return h.Enigma.Validate(ctx, h.trimPrefix(token, "ac")) + return h.Enigma.Validate(ctx, h.trimPrefix(ctx, token, "ac")) } diff --git a/handler/oauth2/strategy_hmacsha_test.go b/handler/oauth2/strategy_hmacsha_test.go index 0d72d0a4f..8f57687f3 100644 --- a/handler/oauth2/strategy_hmacsha_test.go +++ b/handler/oauth2/strategy_hmacsha_test.go @@ -178,3 +178,20 @@ func TestHMACAuthorizeCode(t *testing.T) { }) } } + +func TestHMACTokenPrefix(t *testing.T) { + t.Run("PrefixToken", func(t *testing.T) { + hmacshaStrategy = HMACSHAStrategy{ + Enigma: &hmac.HMACStrategy{Config: &fosite.Config{GlobalSecret: []byte("foobarfoobarfoobarfoobarfoobarfoobarfoobarfoobar")}}, + Config: &fosite.Config{ + AccessTokenLifespan: time.Hour * 24, + AuthorizeCodeLifespan: time.Hour * 24, + TokenPrefix: "abc", + }, + } + token, signature, err := hmacshaStrategy.GenerateAccessToken(nil, &hmacValidCase) + assert.NoError(t, err) + assert.Equal(t, strings.Split(token, ".")[1], signature) + assert.Contains(t, token, "abc_at_") + }) +} diff --git a/handler/openid/flow_hybrid.go b/handler/openid/flow_hybrid.go index e0bdf181c..d154b56ba 100644 --- a/handler/openid/flow_hybrid.go +++ b/handler/openid/flow_hybrid.go @@ -27,6 +27,7 @@ type OpenIDConnectHybridHandler struct { fosite.IDTokenLifespanProvider fosite.MinParameterEntropyProvider fosite.ScopeStrategyProvider + fosite.TokenPrefixProvider } } diff --git a/handler/openid/flow_hybrid_test.go b/handler/openid/flow_hybrid_test.go index 4cba91839..758dd0762 100644 --- a/handler/openid/flow_hybrid_test.go +++ b/handler/openid/flow_hybrid_test.go @@ -32,6 +32,7 @@ var hmacStrategy = &oauth2.HMACSHAStrategy{ GlobalSecret: []byte("some-super-cool-secret-that-nobody-knows-nobody-knows"), }, }, + Config: &fosite.Config{}, } type defaultSession struct { diff --git a/handler/rfc7523/handler.go b/handler/rfc7523/handler.go index 579d8c047..12c619856 100644 --- a/handler/rfc7523/handler.go +++ b/handler/rfc7523/handler.go @@ -24,6 +24,7 @@ type Handler struct { Config interface { fosite.AccessTokenLifespanProvider + fosite.TokenPrefixProvider fosite.TokenURLProvider fosite.GrantTypeJWTBearerCanSkipClientAuthProvider fosite.GrantTypeJWTBearerIDOptionalProvider