package oidc_test import ( "context" "fmt" "net/url" "testing" "time" "authelia.com/provider/oauth2/handler/oauth2" "authelia.com/provider/oauth2/token/jwt" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/authelia/authelia/v4/internal/configuration/schema" "github.com/authelia/authelia/v4/internal/model" "github.com/authelia/authelia/v4/internal/oidc" "github.com/authelia/authelia/v4/internal/templates" ) func TestConfig_GetAllowedPrompts(t *testing.T) { ctx := context.Background() config := &oidc.Config{} assert.Equal(t, []string(nil), config.AllowedPrompts) assert.Equal(t, []string{oidc.PromptNone, oidc.PromptLogin, oidc.PromptConsent, oidc.PromptSelectAccount}, config.GetAllowedPrompts(ctx)) assert.Equal(t, []string{oidc.PromptNone, oidc.PromptLogin, oidc.PromptConsent, oidc.PromptSelectAccount}, config.AllowedPrompts) config.AllowedPrompts = []string{oidc.PromptNone} assert.Equal(t, []string{oidc.PromptNone}, config.AllowedPrompts) } func TestConfig_PKCE(t *testing.T) { ctx := context.Background() config := &oidc.Config{} assert.False(t, config.GetEnforcePKCE(ctx)) assert.False(t, config.GetEnforcePKCEForPublicClients(ctx)) config.ProofKeyCodeExchange.Enforce = true assert.True(t, config.GetEnforcePKCE(ctx)) assert.True(t, config.GetEnforcePKCEForPublicClients(ctx)) config.ProofKeyCodeExchange.Enforce = false assert.False(t, config.GetEnforcePKCEForPublicClients(ctx)) config.ProofKeyCodeExchange.EnforcePublicClients = true assert.True(t, config.GetEnforcePKCEForPublicClients(ctx)) assert.False(t, config.GetEnablePKCEPlainChallengeMethod(ctx)) config.ProofKeyCodeExchange.AllowPlainChallengeMethod = true assert.True(t, config.GetEnablePKCEPlainChallengeMethod(ctx)) } func TestConfig_GrantTypeJWTBearer(t *testing.T) { ctx := context.Background() config := &oidc.Config{} assert.False(t, config.GetGrantTypeJWTBearerIDOptional(ctx)) assert.False(t, config.GetGrantTypeJWTBearerCanSkipClientAuth(ctx)) assert.False(t, config.GetGrantTypeJWTBearerIssuedDateOptional(ctx)) config.GrantTypeJWTBearer.OptionalJTIClaim = true assert.True(t, config.GetGrantTypeJWTBearerIDOptional(ctx)) assert.False(t, config.GetGrantTypeJWTBearerCanSkipClientAuth(ctx)) assert.False(t, config.GetGrantTypeJWTBearerIssuedDateOptional(ctx)) config.GrantTypeJWTBearer.OptionalClientAuth = true assert.True(t, config.GetGrantTypeJWTBearerIDOptional(ctx)) assert.True(t, config.GetGrantTypeJWTBearerCanSkipClientAuth(ctx)) assert.False(t, config.GetGrantTypeJWTBearerIssuedDateOptional(ctx)) config.GrantTypeJWTBearer.OptionalIssuedDate = true assert.True(t, config.GetGrantTypeJWTBearerIDOptional(ctx)) assert.True(t, config.GetGrantTypeJWTBearerCanSkipClientAuth(ctx)) assert.True(t, config.GetGrantTypeJWTBearerIssuedDateOptional(ctx)) } func TestConfig_Durations(t *testing.T) { ctx := context.Background() config := &oidc.Config{} assert.Equal(t, time.Duration(0), config.JWTMaxDuration) assert.Equal(t, time.Hour*24, config.GetJWTMaxDuration(ctx)) assert.Equal(t, time.Hour*24, config.JWTMaxDuration) assert.Equal(t, time.Duration(0), config.Lifespans.IDToken) assert.Equal(t, time.Hour, config.GetIDTokenLifespan(ctx)) assert.Equal(t, time.Hour, config.Lifespans.IDToken) assert.Equal(t, time.Duration(0), config.Lifespans.AccessToken) assert.Equal(t, time.Hour, config.GetAccessTokenLifespan(ctx)) assert.Equal(t, time.Hour, config.Lifespans.AccessToken) assert.Equal(t, time.Duration(0), config.Lifespans.RefreshToken) assert.Equal(t, time.Hour*24*30, config.GetRefreshTokenLifespan(ctx)) assert.Equal(t, time.Hour*24*30, config.Lifespans.RefreshToken) assert.Equal(t, time.Duration(0), config.Lifespans.AuthorizeCode) assert.Equal(t, time.Minute*15, config.GetAuthorizeCodeLifespan(ctx)) assert.Equal(t, time.Minute*15, config.Lifespans.AuthorizeCode) } func TestConfig_GetTokenEntropy(t *testing.T) { ctx := context.Background() config := &oidc.Config{} assert.Equal(t, 0, config.TokenEntropy) assert.Equal(t, 32, config.GetTokenEntropy(ctx)) assert.Equal(t, 32, config.TokenEntropy) } func TestConfig_Misc(t *testing.T) { ctx := context.Background() config := &oidc.Config{} assert.False(t, config.DisableRefreshTokenValidation) assert.False(t, config.GetDisableRefreshTokenValidation(ctx)) assert.Equal(t, "", config.Issuers.AccessToken) assert.Equal(t, "", config.GetAccessTokenIssuer(ctx)) assert.Equal(t, "", config.Issuers.IDToken) assert.Equal(t, "", config.GetIDTokenIssuer(ctx)) assert.Equal(t, jwt.JWTScopeFieldUnset, config.JWTScopeField) assert.Equal(t, jwt.JWTScopeFieldList, config.GetJWTScopeField(ctx)) assert.Equal(t, jwt.JWTScopeFieldList, config.JWTScopeField) assert.Equal(t, []string(nil), config.SanitationWhiteList) assert.Equal(t, []string(nil), config.GetSanitationWhiteList(ctx)) assert.Equal(t, []string(nil), config.SanitationWhiteList) assert.False(t, config.OmitRedirectScopeParameter) assert.False(t, config.GetOmitRedirectScopeParam(ctx)) assert.NotNil(t, config.GetRedirectSecureChecker(ctx)) assert.NotNil(t, config.GetHTTPClient(ctx)) assert.Nil(t, config.Strategy.Scope) assert.NotNil(t, config.GetScopeStrategy(ctx)) assert.NotNil(t, config.Strategy.Scope) assert.Nil(t, config.Strategy.Audience) assert.NotNil(t, config.GetAudienceStrategy(ctx)) assert.NotNil(t, config.Strategy.Audience) assert.Equal(t, []string(nil), config.RefreshTokenScopes) assert.Equal(t, []string{oidc.ScopeOffline, oidc.ScopeOfflineAccess}, config.GetRefreshTokenScopes(ctx)) assert.Equal(t, []string{oidc.ScopeOffline, oidc.ScopeOfflineAccess}, config.RefreshTokenScopes) assert.Equal(t, 0, config.MinParameterEntropy) assert.Equal(t, 8, config.GetMinParameterEntropy(ctx)) assert.Equal(t, 8, config.MinParameterEntropy) assert.False(t, config.SendDebugMessagesToClients) assert.False(t, config.GetSendDebugMessagesToClients(ctx)) config.SendDebugMessagesToClients = true assert.True(t, config.GetSendDebugMessagesToClients(ctx)) assert.Nil(t, config.Strategy.JWKSFetcher) assert.NotNil(t, config.GetJWKSFetcherStrategy(ctx)) assert.NotNil(t, config.Strategy.JWKSFetcher) assert.Nil(t, config.Strategy.ClientAuthentication) assert.Nil(t, config.GetClientAuthenticationStrategy(ctx)) assert.Nil(t, config.MessageCatalog) assert.Nil(t, config.GetMessageCatalog(ctx)) assert.Nil(t, config.Templates) assert.Nil(t, config.GetFormPostHTMLTemplate(ctx)) var err error config.Templates, err = templates.New(templates.Config{}) require.NoError(t, err) assert.NotNil(t, config.GetFormPostHTMLTemplate(ctx)) assert.NotNil(t, config.Templates) assert.False(t, config.GetUseLegacyErrorFormat(ctx)) assert.Nil(t, config.GetAuthorizeEndpointHandlers(ctx)) assert.Nil(t, config.GetTokenEndpointHandlers(ctx)) assert.Nil(t, config.GetTokenIntrospectionHandlers(ctx)) assert.Nil(t, config.GetRevocationHandlers(ctx)) assert.Nil(t, config.GetPushedAuthorizeEndpointHandlers(ctx)) assert.Equal(t, []string(nil), config.GetAllowedJWTAssertionAudiences(ctx)) var octx context.Context octx = &TestContext{ Context: ctx, IssuerURLFunc: func() (issuerURL *url.URL, err error) { return nil, fmt.Errorf("test error") }, } octx = context.WithValue(octx, model.CtxKeyAutheliaCtx, octx) assert.Equal(t, []string(nil), config.GetAllowedJWTAssertionAudiences(octx)) } func TestConfig_PAR(t *testing.T) { ctx := context.Background() config := &oidc.Config{} assert.Equal(t, "", config.PAR.URIPrefix) assert.Equal(t, "urn:ietf:params:oauth:request_uri:", config.GetPushedAuthorizeRequestURIPrefix(ctx)) assert.Equal(t, "urn:ietf:params:oauth:request_uri:", config.PAR.URIPrefix) assert.False(t, config.PAR.Require) assert.False(t, config.GetRequirePushedAuthorizationRequests(ctx)) assert.False(t, config.PAR.Require) config.PAR.Require = true assert.True(t, config.GetRequirePushedAuthorizationRequests(ctx)) assert.Equal(t, time.Duration(0), config.PAR.ContextLifespan) assert.Equal(t, time.Minute*5, config.GetPushedAuthorizeContextLifespan(ctx)) assert.Equal(t, time.Minute*5, config.PAR.ContextLifespan) } func TestNewConfig(t *testing.T) { c := &schema.IdentityProvidersOpenIDConnect{ Discovery: schema.IdentityProvidersOpenIDConnectDiscovery{ JWTResponseAccessTokens: true, }, } tmpl, err := templates.New(templates.Config{}) require.NoError(t, err) strategy := oidc.NewIssuer(c.JSONWebKeys) config := oidc.NewConfig(c, strategy, tmpl) assert.IsType(t, &oauth2.JWTProfileCoreStrategy{}, config.Strategy.Core) config.LoadHandlers(nil) assert.Len(t, config.Handlers.TokenIntrospection, 1) config.JWTAccessToken.EnableStatelessIntrospection = true config.LoadHandlers(nil) assert.Len(t, config.Handlers.TokenIntrospection, 2) } func TestConfig_GetIssuerFuncs(t *testing.T) { testCases := []struct { name string have oidc.IssuersConfig ctx context.Context expectIntrospection, expectIDToken, expectAccessToken, expectAS, expectJARM string }{ { "ShouldReturnCtxValues", oidc.IssuersConfig{}, &TestContext{ Context: context.Background(), IssuerURLFunc: func() (issuerURL *url.URL, err error) { return &url.URL{Scheme: "https", Host: "example.com", Path: "/issuer"}, nil }, }, "https://example.com/issuer", "https://example.com/issuer", "https://example.com/issuer", "https://example.com/issuer", "https://example.com/issuer", }, { "ShouldNotReturnDefaultValues", oidc.IssuersConfig{ IDToken: "https://example.com/id-issuer", }, &TestContext{ Context: context.Background(), IssuerURLFunc: func() (issuerURL *url.URL, err error) { return &url.URL{Scheme: "https", Host: "example.com", Path: "/issuer"}, nil }, }, "https://example.com/issuer", "https://example.com/issuer", "https://example.com/issuer", "https://example.com/issuer", "https://example.com/issuer", }, { "ShouldReturnDefaultValues", oidc.IssuersConfig{ IDToken: "https://example.com/id-issuer", AccessToken: "https://example.com/at-issuer", Introspection: "https://example.com/i-issuer", JWTSecuredResponseMode: "https://example.com/jarm-issuer", AuthorizationServerIssuerIdentification: "https://example.com/as-issuer", }, context.Background(), "https://example.com/i-issuer", "https://example.com/id-issuer", "https://example.com/at-issuer", "https://example.com/as-issuer", "https://example.com/jarm-issuer", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { config := &oidc.Config{ Issuers: tc.have, } ctx := context.WithValue(tc.ctx, 0, 1) //nolint:staticcheck // This value is used to demonstrate a functionality not used for an actual value. assert.Equal(t, tc.expectIntrospection, config.GetIntrospectionIssuer(ctx)) assert.Equal(t, tc.expectIDToken, config.GetIDTokenIssuer(ctx)) assert.Equal(t, tc.expectAccessToken, config.GetAccessTokenIssuer(ctx)) assert.Equal(t, tc.expectAS, config.GetAuthorizationServerIdentificationIssuer(ctx)) assert.Equal(t, tc.expectJARM, config.GetJWTSecuredAuthorizeResponseModeIssuer(ctx)) }) } } func TestMisc(t *testing.T) { tctx := &TestContext{ Context: context.Background(), IssuerURLFunc: func() (issuerURL *url.URL, err error) { return &url.URL{Scheme: "https", Host: "example.com", Path: "/issuer"}, nil }, } config := &oidc.Config{} assert.Nil(t, config.GetIntrospectionJWTResponseStrategy(context.Background())) assert.Nil(t, config.GetJWTSecuredAuthorizeResponseModeStrategy(context.Background())) secret, err := config.GetGlobalSecret(context.Background()) assert.NoError(t, err) assert.Nil(t, secret) secrets, err := config.GetRotatedGlobalSecrets(context.Background()) assert.NoError(t, err) assert.Nil(t, secrets) assert.Equal(t, time.Minute*5, config.GetJWTSecuredAuthorizeResponseModeLifespan(context.Background())) assert.False(t, config.GetRevokeRefreshTokensExplicit(context.Background())) assert.False(t, config.GetEnforceRevokeFlowRevokeRefreshTokensExplicitClient(context.Background())) assert.False(t, config.GetClientCredentialsFlowImplicitGrantRequested(context.Background())) assert.False(t, config.GetEnforceJWTProfileAccessTokens(context.Background())) config.ClientCredentialsFlowImplicitGrantRequested = true assert.True(t, config.GetClientCredentialsFlowImplicitGrantRequested(context.Background())) assert.NotNil(t, config.GetHMACHasher(context.Background())) assert.NotNil(t, config.GetFormPostResponseWriter(context.Background())) assert.Equal(t, time.Hour, config.GetVerifiableCredentialsNonceLifespan(context.Background())) assert.Nil(t, config.GetResponseModeHandlers(context.Background())) assert.Nil(t, config.GetResponseModeParameterHandlers(context.Background())) assert.Nil(t, config.GetRFC8628DeviceAuthorizeEndpointHandlers(context.Background())) assert.Nil(t, config.GetRFC8628UserAuthorizeEndpointHandlers(context.Background())) assert.Nil(t, config.GetRFC8693TokenTypes(context.Background())) assert.Equal(t, "", config.GetDefaultRFC8693RequestedTokenType(context.Background())) assert.Equal(t, time.Minute*10, config.GetRFC8628CodeLifespan(context.Background())) assert.Equal(t, time.Second*10, config.GetRFC8628TokenPollingInterval(context.Background())) assert.Equal(t, []string{"https://example.com/issuer", "https://example.com/issuer/api/oidc/token", "https://example.com/issuer/api/oidc/pushed-authorization-request"}, config.GetAllowedJWTAssertionAudiences(tctx)) assert.Equal(t, "https://example.com/issuer/consent/openid/device-authorization", config.GetRFC8628UserVerificationURL(tctx)) }