summaryrefslogtreecommitdiff
path: root/internal/configuration/validator/identity_providers_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/configuration/validator/identity_providers_test.go')
-rw-r--r--internal/configuration/validator/identity_providers_test.go1377
1 files changed, 1185 insertions, 192 deletions
diff --git a/internal/configuration/validator/identity_providers_test.go b/internal/configuration/validator/identity_providers_test.go
index 8957c61f8..bdb11f3d0 100644
--- a/internal/configuration/validator/identity_providers_test.go
+++ b/internal/configuration/validator/identity_providers_test.go
@@ -7,7 +7,6 @@ import (
"errors"
"fmt"
"net/url"
- "strings"
"testing"
"time"
@@ -31,8 +30,8 @@ func TestShouldRaiseErrorWhenInvalidOIDCServerConfiguration(t *testing.T) {
require.Len(t, validator.Errors(), 2)
- assert.EqualError(t, validator.Errors()[0], errFmtOIDCNoPrivateKey)
- assert.EqualError(t, validator.Errors()[1], errFmtOIDCNoClientsConfigured)
+ assert.EqualError(t, validator.Errors()[0], "identity_providers: oidc: option 'issuer_private_key' is required")
+ assert.EqualError(t, validator.Errors()[1], "identity_providers: oidc: option 'clients' must have one or more clients configured")
}
func TestShouldNotRaiseErrorWhenCORSEndpointsValid(t *testing.T) {
@@ -80,7 +79,7 @@ func TestShouldRaiseErrorWhenCORSEndpointsNotValid(t *testing.T) {
require.Len(t, validator.Errors(), 1)
- assert.EqualError(t, validator.Errors()[0], "identity_providers: oidc: cors: option 'endpoints' contains an invalid value 'invalid_endpoint': must be one of 'authorization', 'pushed-authorization-request', 'token', 'introspection', 'revocation', 'userinfo'")
+ assert.EqualError(t, validator.Errors()[0], "identity_providers: oidc: cors: option 'endpoints' contains an invalid value 'invalid_endpoint': must be one of 'authorization', 'pushed-authorization-request', 'token', 'introspection', 'revocation', or 'userinfo'")
}
func TestShouldRaiseErrorWhenOIDCPKCEEnforceValueInvalid(t *testing.T) {
@@ -97,8 +96,8 @@ func TestShouldRaiseErrorWhenOIDCPKCEEnforceValueInvalid(t *testing.T) {
require.Len(t, validator.Errors(), 2)
- assert.EqualError(t, validator.Errors()[0], "identity_providers: oidc: option 'enforce_pkce' must be 'never', 'public_clients_only' or 'always', but it is configured as 'invalid'")
- assert.EqualError(t, validator.Errors()[1], errFmtOIDCNoClientsConfigured)
+ assert.EqualError(t, validator.Errors()[0], "identity_providers: oidc: option 'enforce_pkce' must be 'never', 'public_clients_only' or 'always', but it's configured as 'invalid'")
+ assert.EqualError(t, validator.Errors()[1], "identity_providers: oidc: option 'clients' must have one or more clients configured")
}
func TestShouldRaiseErrorWhenOIDCCORSOriginsHasInvalidValues(t *testing.T) {
@@ -150,7 +149,7 @@ func TestShouldRaiseErrorWhenOIDCServerNoClients(t *testing.T) {
require.Len(t, validator.Errors(), 1)
- assert.EqualError(t, validator.Errors()[0], errFmtOIDCNoClientsConfigured)
+ assert.EqualError(t, validator.Errors()[0], "identity_providers: oidc: option 'clients' must have one or more clients configured")
}
func TestShouldRaiseErrorWhenOIDCServerClientBadValues(t *testing.T) {
@@ -180,7 +179,7 @@ func TestShouldRaiseErrorWhenOIDCServerClientBadValues(t *testing.T) {
},
Errors: []string{
"identity_providers: oidc: client '': option 'secret' is required",
- "identity_providers: oidc: one or more clients have been configured with an empty id",
+ "identity_providers: oidc: clients: option 'id' is required but was absent on the clients in positions #1",
},
},
{
@@ -195,7 +194,9 @@ func TestShouldRaiseErrorWhenOIDCServerClientBadValues(t *testing.T) {
},
},
},
- Errors: []string{"identity_providers: oidc: client 'client-1': option 'policy' must be 'one_factor' or 'two_factor' but it is configured as 'a-policy'"},
+ Errors: []string{
+ "identity_providers: oidc: client 'client-1': option 'policy' must be one of 'one_factor' or 'two_factor' but it's configured as 'a-policy'",
+ },
},
{
Name: "ClientIDDuplicated",
@@ -213,7 +214,9 @@ func TestShouldRaiseErrorWhenOIDCServerClientBadValues(t *testing.T) {
RedirectURIs: []string{},
},
},
- Errors: []string{errFmtOIDCClientsDuplicateID},
+ Errors: []string{
+ "identity_providers: oidc: clients: option 'id' must be unique for every client but one or more clients share the following 'id' values 'client-x'",
+ },
},
{
Name: "RedirectURIInvalid",
@@ -228,7 +231,7 @@ func TestShouldRaiseErrorWhenOIDCServerClientBadValues(t *testing.T) {
},
},
Errors: []string{
- fmt.Sprintf(errFmtOIDCClientRedirectURICantBeParsed, "client-check-uri-parse", "http://abc@%two", errors.New("parse \"http://abc@%two\": invalid URL escape \"%tw\"")),
+ "identity_providers: oidc: client 'client-check-uri-parse': option 'redirect_uris' has an invalid value: redirect uri 'http://abc@%two' could not be parsed: parse \"http://abc@%two\": invalid URL escape \"%tw\"",
},
},
{
@@ -244,7 +247,7 @@ func TestShouldRaiseErrorWhenOIDCServerClientBadValues(t *testing.T) {
},
},
Errors: []string{
- fmt.Sprintf(errFmtOIDCClientRedirectURIAbsolute, "client-check-uri-abs", "google.com"),
+ "identity_providers: oidc: client 'client-check-uri-abs': option 'redirect_uris' has an invalid value: redirect uri 'google.com' must have a scheme but it's absent",
},
},
{
@@ -289,12 +292,12 @@ func TestShouldRaiseErrorWhenOIDCServerClientBadValues(t *testing.T) {
},
},
Errors: []string{
- fmt.Sprintf(errFmtOIDCClientInvalidSectorIdentifier, "client-invalid-sector", "https://user:pass@example.com/path?query=abc#fragment", exampleDotCom, "scheme", "https"),
- fmt.Sprintf(errFmtOIDCClientInvalidSectorIdentifier, "client-invalid-sector", "https://user:pass@example.com/path?query=abc#fragment", exampleDotCom, "path", "/path"),
- fmt.Sprintf(errFmtOIDCClientInvalidSectorIdentifier, "client-invalid-sector", "https://user:pass@example.com/path?query=abc#fragment", exampleDotCom, "query", "query=abc"),
- fmt.Sprintf(errFmtOIDCClientInvalidSectorIdentifier, "client-invalid-sector", "https://user:pass@example.com/path?query=abc#fragment", exampleDotCom, "fragment", "fragment"),
- fmt.Sprintf(errFmtOIDCClientInvalidSectorIdentifier, "client-invalid-sector", "https://user:pass@example.com/path?query=abc#fragment", exampleDotCom, "username", "user"),
- fmt.Sprintf(errFmtOIDCClientInvalidSectorIdentifierWithoutValue, "client-invalid-sector", "https://user:pass@example.com/path?query=abc#fragment", exampleDotCom, "password"),
+ "identity_providers: oidc: client 'client-invalid-sector': option 'sector_identifier' with value 'https://user:pass@example.com/path?query=abc#fragment': must be a URL with only the host component for example 'example.com' but it has a scheme with the value 'https'",
+ "identity_providers: oidc: client 'client-invalid-sector': option 'sector_identifier' with value 'https://user:pass@example.com/path?query=abc#fragment': must be a URL with only the host component for example 'example.com' but it has a path with the value '/path'",
+ "identity_providers: oidc: client 'client-invalid-sector': option 'sector_identifier' with value 'https://user:pass@example.com/path?query=abc#fragment': must be a URL with only the host component for example 'example.com' but it has a query with the value 'query=abc'",
+ "identity_providers: oidc: client 'client-invalid-sector': option 'sector_identifier' with value 'https://user:pass@example.com/path?query=abc#fragment': must be a URL with only the host component for example 'example.com' but it has a fragment with the value 'fragment'",
+ "identity_providers: oidc: client 'client-invalid-sector': option 'sector_identifier' with value 'https://user:pass@example.com/path?query=abc#fragment': must be a URL with only the host component for example 'example.com' but it has a username with the value 'user'",
+ "identity_providers: oidc: client 'client-invalid-sector': option 'sector_identifier' with value 'https://user:pass@example.com/path?query=abc#fragment': must be a URL with only the host component for example 'example.com' but it has a password",
},
},
{
@@ -311,7 +314,7 @@ func TestShouldRaiseErrorWhenOIDCServerClientBadValues(t *testing.T) {
},
},
Errors: []string{
- fmt.Sprintf(errFmtOIDCClientInvalidSectorIdentifierHost, "client-invalid-sector", "example.com/path?query=abc#fragment"),
+ "identity_providers: oidc: client 'client-invalid-sector': option 'sector_identifier' with value 'example.com/path?query=abc#fragment': must be a URL with only the host component but appears to be invalid",
},
},
{
@@ -328,7 +331,7 @@ func TestShouldRaiseErrorWhenOIDCServerClientBadValues(t *testing.T) {
},
},
Errors: []string{
- fmt.Sprintf(errFmtOIDCClientInvalidConsentMode, "client-bad-consent-mode", strings.Join(append(validOIDCClientConsentModes, "auto"), "', '"), "cap"),
+ "identity_providers: oidc: client 'client-bad-consent-mode': consent: option 'mode' must be one of 'auto', 'implicit', 'explicit', 'pre-configured', or 'auto' but it's configured as 'cap'",
},
},
{
@@ -345,7 +348,7 @@ func TestShouldRaiseErrorWhenOIDCServerClientBadValues(t *testing.T) {
},
},
Errors: []string{
- fmt.Sprintf(errFmtOIDCClientInvalidPKCEChallengeMethod, "client-bad-pkce-mode", "abc"),
+ "identity_providers: oidc: client 'client-bad-pkce-mode': option 'pkce_challenge_method' must be one of 'plain' or 'S256' but it's configured as 'abc'",
},
},
{
@@ -362,7 +365,7 @@ func TestShouldRaiseErrorWhenOIDCServerClientBadValues(t *testing.T) {
},
},
Errors: []string{
- fmt.Sprintf(errFmtOIDCClientInvalidPKCEChallengeMethod, "client-bad-pkce-mode-s256", "s256"),
+ "identity_providers: oidc: client 'client-bad-pkce-mode-s256': option 'pkce_challenge_method' must be one of 'plain' or 'S256' but it's configured as 's256'",
},
},
}
@@ -415,7 +418,7 @@ func TestShouldRaiseErrorWhenOIDCClientConfiguredWithBadScopes(t *testing.T) {
ValidateIdentityProviders(config, validator)
require.Len(t, validator.Errors(), 1)
- assert.EqualError(t, validator.Errors()[0], "identity_providers: oidc: client 'good_id': option 'scopes' must only have the values 'openid', 'email', 'profile', 'groups', 'offline_access' but one option is configured as 'bad_scope'")
+ assert.EqualError(t, validator.Errors()[0], "identity_providers: oidc: client 'good_id': option 'scopes' must only have the values 'openid', 'email', 'profile', 'groups', or 'offline_access' but the values 'bad_scope' are present")
}
func TestShouldRaiseErrorWhenOIDCClientConfiguredWithBadGrantTypes(t *testing.T) {
@@ -441,7 +444,7 @@ func TestShouldRaiseErrorWhenOIDCClientConfiguredWithBadGrantTypes(t *testing.T)
ValidateIdentityProviders(config, validator)
require.Len(t, validator.Errors(), 1)
- assert.EqualError(t, validator.Errors()[0], "identity_providers: oidc: client 'good_id': option 'grant_types' must only have the values 'implicit', 'refresh_token', 'authorization_code', 'password', 'client_credentials' but one option is configured as 'bad_grant_type'")
+ assert.EqualError(t, validator.Errors()[0], "identity_providers: oidc: client 'good_id': option 'grant_types' must only have the values 'implicit', 'refresh_token', or 'authorization_code' but the values 'bad_grant_type' are present")
}
func TestShouldNotErrorOnCertificateValid(t *testing.T) {
@@ -577,7 +580,7 @@ func TestShouldRaiseErrorWhenOIDCClientConfiguredWithBadResponseModes(t *testing
ValidateIdentityProviders(config, validator)
require.Len(t, validator.Errors(), 1)
- assert.EqualError(t, validator.Errors()[0], "identity_providers: oidc: client 'good_id': option 'response_modes' must only have the values 'form_post', 'query', 'fragment' but one option is configured as 'bad_responsemode'")
+ assert.EqualError(t, validator.Errors()[0], "identity_providers: oidc: client 'good_id': option 'response_modes' must only have the values 'form_post', 'query', or 'fragment' but the values 'bad_responsemode' are present")
}
func TestShouldRaiseErrorWhenOIDCClientConfiguredWithBadUserinfoAlg(t *testing.T) {
@@ -603,7 +606,7 @@ func TestShouldRaiseErrorWhenOIDCClientConfiguredWithBadUserinfoAlg(t *testing.T
ValidateIdentityProviders(config, validator)
require.Len(t, validator.Errors(), 1)
- assert.EqualError(t, validator.Errors()[0], "identity_providers: oidc: client 'good_id': option 'userinfo_signing_algorithm' must be one of 'none, RS256' but it is configured as 'rs256'")
+ assert.EqualError(t, validator.Errors()[0], "identity_providers: oidc: client 'good_id': option 'userinfo_signing_algorithm' must be one of 'none' or 'RS256' but it's configured as 'rs256'")
}
func TestValidateIdentityProvidersShouldRaiseWarningOnSecurityIssue(t *testing.T) {
@@ -668,8 +671,8 @@ func TestValidateIdentityProvidersShouldRaiseErrorsOnInvalidClientTypes(t *testi
require.Len(t, validator.Errors(), 2)
assert.Len(t, validator.Warnings(), 0)
- assert.EqualError(t, validator.Errors()[0], fmt.Sprintf(errFmtOIDCClientPublicInvalidSecret, "client-with-invalid-secret"))
- assert.EqualError(t, validator.Errors()[1], fmt.Sprintf(errFmtOIDCClientRedirectURIPublic, "client-with-bad-redirect-uri", oauth2InstalledApp))
+ assert.EqualError(t, validator.Errors()[0], "identity_providers: oidc: client 'client-with-invalid-secret': option 'secret' is required to be empty when option 'public' is true")
+ assert.EqualError(t, validator.Errors()[1], "identity_providers: oidc: client 'client-with-bad-redirect-uri': option 'redirect_uris' has the redirect uri 'urn:ietf:wg:oauth:2.0:oob' when option 'public' is false but this is invalid as this uri is not valid for the openid connect confidential client type")
}
func TestValidateIdentityProvidersShouldNotRaiseErrorsOnValidClientOptions(t *testing.T) {
@@ -758,175 +761,28 @@ func TestValidateIdentityProvidersShouldRaiseWarningOnPlainTextClients(t *testin
assert.EqualError(t, validator.Warnings()[0], "identity_providers: oidc: client 'client-with-invalid-secret_standard': option 'secret' is plaintext but it should be a hashed value as plaintext values are deprecated and will be removed when oidc becomes stable")
}
-func TestValidateIdentityProvidersShouldSetDefaultValues(t *testing.T) {
- timeDay := time.Hour * 24
-
- validator := schema.NewStructValidator()
- config := &schema.IdentityProvidersConfiguration{
- OIDC: &schema.OpenIDConnectConfiguration{
- HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
- IssuerPrivateKey: MustParseRSAPrivateKey(testKey1),
- Clients: []schema.OpenIDConnectClientConfiguration{
- {
- ID: "a-client",
- Secret: MustDecodeSecret(goodOpenIDConnectClientSecret),
- RedirectURIs: []string{
- "https://google.com",
- },
- ConsentPreConfiguredDuration: &timeDay,
- },
- {
- ID: "b-client",
- Description: "Normal Description",
- Secret: MustDecodeSecret(goodOpenIDConnectClientSecret),
- Policy: policyOneFactor,
- UserinfoSigningAlgorithm: "RS256",
- RedirectURIs: []string{
- "https://google.com",
- },
- Scopes: []string{
- "groups",
- },
- GrantTypes: []string{
- "refresh_token",
- },
- ResponseTypes: []string{
- "token",
- "code",
- },
- ResponseModes: []string{
- "form_post",
- "fragment",
- },
- },
- {
- ID: "c-client",
- Secret: MustDecodeSecret(goodOpenIDConnectClientSecret),
- RedirectURIs: []string{
- "https://google.com",
- },
- ConsentMode: "implicit",
- },
- {
- ID: "d-client",
- Secret: MustDecodeSecret(goodOpenIDConnectClientSecret),
- RedirectURIs: []string{
- "https://google.com",
- },
- ConsentMode: "explicit",
- },
- {
- ID: "e-client",
- Secret: MustDecodeSecret(goodOpenIDConnectClientSecret),
- RedirectURIs: []string{
- "https://google.com",
- },
- ConsentMode: "pre-configured",
- },
- },
- },
- }
-
- ValidateIdentityProviders(config, validator)
-
- assert.Len(t, validator.Warnings(), 0)
- assert.Len(t, validator.Errors(), 0)
-
- // Assert Clients[0] Policy is set to the default, and the default doesn't override Clients[1]'s Policy.
- assert.Equal(t, policyTwoFactor, config.OIDC.Clients[0].Policy)
- assert.Equal(t, policyOneFactor, config.OIDC.Clients[1].Policy)
-
- assert.Equal(t, "none", config.OIDC.Clients[0].UserinfoSigningAlgorithm)
- assert.Equal(t, "RS256", config.OIDC.Clients[1].UserinfoSigningAlgorithm)
-
- // Assert Clients[0] Description is set to the Clients[0] ID, and Clients[1]'s Description is not overridden.
- assert.Equal(t, config.OIDC.Clients[0].ID, config.OIDC.Clients[0].Description)
- assert.Equal(t, "Normal Description", config.OIDC.Clients[1].Description)
-
- // Assert Clients[0] ends up configured with the default Scopes.
- require.Len(t, config.OIDC.Clients[0].Scopes, 4)
- assert.Equal(t, "openid", config.OIDC.Clients[0].Scopes[0])
- assert.Equal(t, "groups", config.OIDC.Clients[0].Scopes[1])
- assert.Equal(t, "profile", config.OIDC.Clients[0].Scopes[2])
- assert.Equal(t, "email", config.OIDC.Clients[0].Scopes[3])
-
- // Assert Clients[1] ends up configured with the configured Scopes and the openid Scope.
- require.Len(t, config.OIDC.Clients[1].Scopes, 2)
- assert.Equal(t, "groups", config.OIDC.Clients[1].Scopes[0])
- assert.Equal(t, "openid", config.OIDC.Clients[1].Scopes[1])
-
- // Assert Clients[0] ends up configured with the correct consent mode.
- require.NotNil(t, config.OIDC.Clients[0].ConsentPreConfiguredDuration)
- assert.Equal(t, time.Hour*24, *config.OIDC.Clients[0].ConsentPreConfiguredDuration)
- assert.Equal(t, "pre-configured", config.OIDC.Clients[0].ConsentMode)
-
- // Assert Clients[1] ends up configured with the correct consent mode.
- assert.Nil(t, config.OIDC.Clients[1].ConsentPreConfiguredDuration)
- assert.Equal(t, "explicit", config.OIDC.Clients[1].ConsentMode)
-
- // Assert Clients[0] ends up configured with the default GrantTypes.
- require.Len(t, config.OIDC.Clients[0].GrantTypes, 2)
- assert.Equal(t, "refresh_token", config.OIDC.Clients[0].GrantTypes[0])
- assert.Equal(t, "authorization_code", config.OIDC.Clients[0].GrantTypes[1])
-
- // Assert Clients[1] ends up configured with only the configured GrantTypes.
- require.Len(t, config.OIDC.Clients[1].GrantTypes, 1)
- assert.Equal(t, "refresh_token", config.OIDC.Clients[1].GrantTypes[0])
-
- // Assert Clients[0] ends up configured with the default ResponseTypes.
- require.Len(t, config.OIDC.Clients[0].ResponseTypes, 1)
- assert.Equal(t, "code", config.OIDC.Clients[0].ResponseTypes[0])
-
- // Assert Clients[1] ends up configured only with the configured ResponseTypes.
- require.Len(t, config.OIDC.Clients[1].ResponseTypes, 2)
- assert.Equal(t, "token", config.OIDC.Clients[1].ResponseTypes[0])
- assert.Equal(t, "code", config.OIDC.Clients[1].ResponseTypes[1])
-
- // Assert Clients[0] ends up configured with the default ResponseModes.
- require.Len(t, config.OIDC.Clients[0].ResponseModes, 3)
- assert.Equal(t, "form_post", config.OIDC.Clients[0].ResponseModes[0])
- assert.Equal(t, "query", config.OIDC.Clients[0].ResponseModes[1])
- assert.Equal(t, "fragment", config.OIDC.Clients[0].ResponseModes[2])
-
- // Assert Clients[1] ends up configured only with the configured ResponseModes.
- require.Len(t, config.OIDC.Clients[1].ResponseModes, 2)
- assert.Equal(t, "form_post", config.OIDC.Clients[1].ResponseModes[0])
- assert.Equal(t, "fragment", config.OIDC.Clients[1].ResponseModes[1])
-
- assert.Equal(t, false, config.OIDC.EnableClientDebugMessages)
- assert.Equal(t, time.Hour, config.OIDC.AccessTokenLifespan)
- assert.Equal(t, time.Minute, config.OIDC.AuthorizeCodeLifespan)
- assert.Equal(t, time.Hour, config.OIDC.IDTokenLifespan)
- assert.Equal(t, time.Minute*90, config.OIDC.RefreshTokenLifespan)
-
- assert.Equal(t, "implicit", config.OIDC.Clients[2].ConsentMode)
- assert.Nil(t, config.OIDC.Clients[2].ConsentPreConfiguredDuration)
-
- assert.Equal(t, "explicit", config.OIDC.Clients[3].ConsentMode)
- assert.Nil(t, config.OIDC.Clients[3].ConsentPreConfiguredDuration)
-
- assert.Equal(t, "pre-configured", config.OIDC.Clients[4].ConsentMode)
- assert.Equal(t, schema.DefaultOpenIDConnectClientConfiguration.ConsentPreConfiguredDuration, config.OIDC.Clients[4].ConsentPreConfiguredDuration)
-}
-
// All valid schemes are supported as defined in https://datatracker.ietf.org/doc/html/rfc8252#section-7.1
func TestValidateOIDCClientRedirectURIsSupportingPrivateUseURISchemes(t *testing.T) {
- conf := schema.OpenIDConnectClientConfiguration{
- ID: "owncloud",
- RedirectURIs: []string{
- "https://www.mywebsite.com",
- "http://www.mywebsite.com",
- "oc://ios.owncloud.com",
- // example given in the RFC https://datatracker.ietf.org/doc/html/rfc8252#section-7.1
- "com.example.app:/oauth2redirect/example-provider",
- oauth2InstalledApp,
+ have := &schema.OpenIDConnectConfiguration{
+ Clients: []schema.OpenIDConnectClientConfiguration{
+ {
+ ID: "owncloud",
+ RedirectURIs: []string{
+ "https://www.mywebsite.com",
+ "http://www.mywebsite.com",
+ "oc://ios.owncloud.com",
+ // example given in the RFC https://datatracker.ietf.org/doc/html/rfc8252#section-7.1
+ "com.example.app:/oauth2redirect/example-provider",
+ oauth2InstalledApp,
+ },
+ },
},
}
t.Run("public", func(t *testing.T) {
validator := schema.NewStructValidator()
- conf.Public = true
- validateOIDCClientRedirectURIs(conf, validator)
+ have.Clients[0].Public = true
+ validateOIDCClientRedirectURIs(0, have, validator, nil)
assert.Len(t, validator.Warnings(), 0)
assert.Len(t, validator.Errors(), 0)
@@ -934,8 +790,8 @@ func TestValidateOIDCClientRedirectURIsSupportingPrivateUseURISchemes(t *testing
t.Run("not public", func(t *testing.T) {
validator := schema.NewStructValidator()
- conf.Public = false
- validateOIDCClientRedirectURIs(conf, validator)
+ have.Clients[0].Public = false
+ validateOIDCClientRedirectURIs(0, have, validator, nil)
assert.Len(t, validator.Warnings(), 0)
assert.Len(t, validator.Errors(), 1)
@@ -945,6 +801,1143 @@ func TestValidateOIDCClientRedirectURIsSupportingPrivateUseURISchemes(t *testing
})
}
+func TestValidateOIDCClients(t *testing.T) {
+ type tcv struct {
+ Scopes []string
+ ResponseTypes []string
+ ResponseModes []string
+ GrantTypes []string
+ }
+
+ testCasses := []struct {
+ name string
+ setup func(have *schema.OpenIDConnectConfiguration)
+ validate func(t *testing.T, have *schema.OpenIDConnectConfiguration)
+ have tcv
+ expected tcv
+ serrs []string // Soft errors which will be warnings before GA.
+ errs []string
+ }{
+ {
+ "ShouldSetDefaultResponseTypeAndResponseModes",
+ nil,
+ nil,
+ tcv{
+ nil,
+ nil,
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery},
+ []string{oidc.GrantTypeAuthorizationCode},
+ },
+ nil,
+ nil,
+ },
+ {
+ "ShouldIncludeMinimalScope",
+ nil,
+ nil,
+ tcv{
+ []string{oidc.ScopeEmail},
+ nil,
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery},
+ []string{oidc.GrantTypeAuthorizationCode},
+ },
+ nil,
+ nil,
+ },
+ {
+ "ShouldSetDefaultResponseModesFlowAuthorizeCode",
+ nil,
+ nil,
+ tcv{
+ nil,
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery},
+ []string{oidc.GrantTypeAuthorizationCode},
+ },
+ nil,
+ nil,
+ },
+ {
+ "ShouldSetDefaultResponseModesFlowImplicit",
+ nil,
+ nil,
+ tcv{
+ nil,
+ []string{oidc.ResponseTypeImplicitFlowBoth},
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeImplicitFlowBoth},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeFragment},
+ []string{oidc.GrantTypeImplicit},
+ },
+ nil,
+ nil,
+ },
+ {
+ "ShouldSetDefaultResponseModesFlowHybrid",
+ nil,
+ nil,
+ tcv{
+ nil,
+ []string{oidc.ResponseTypeHybridFlowBoth},
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeHybridFlowBoth},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeFragment},
+ []string{oidc.GrantTypeAuthorizationCode, oidc.GrantTypeImplicit},
+ },
+ nil,
+ nil,
+ },
+ {
+ "ShouldSetDefaultResponseModesFlowMixedAuthorizeCodeHybrid",
+ nil,
+ nil,
+ tcv{
+ nil,
+ []string{oidc.ResponseTypeAuthorizationCodeFlow, oidc.ResponseTypeHybridFlowBoth},
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow, oidc.ResponseTypeHybridFlowBoth},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery, oidc.ResponseModeFragment},
+ []string{oidc.GrantTypeAuthorizationCode, oidc.GrantTypeImplicit},
+ },
+ nil,
+ nil,
+ },
+ {
+ "ShouldSetDefaultResponseModesFlowMixedAuthorizeCodeImplicit",
+ nil,
+ nil,
+ tcv{
+ nil,
+ []string{oidc.ResponseTypeAuthorizationCodeFlow, oidc.ResponseTypeImplicitFlowBoth},
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow, oidc.ResponseTypeImplicitFlowBoth},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery, oidc.ResponseModeFragment},
+ []string{oidc.GrantTypeAuthorizationCode, oidc.GrantTypeImplicit},
+ },
+ nil,
+ nil,
+ },
+ {
+ "ShouldSetDefaultResponseModesFlowMixedAll",
+ nil,
+ nil,
+ tcv{
+ nil,
+ []string{oidc.ResponseTypeAuthorizationCodeFlow, oidc.ResponseTypeImplicitFlowBoth, oidc.ResponseTypeHybridFlowBoth},
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow, oidc.ResponseTypeImplicitFlowBoth, oidc.ResponseTypeHybridFlowBoth},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery, oidc.ResponseModeFragment},
+ []string{oidc.GrantTypeAuthorizationCode, oidc.GrantTypeImplicit},
+ },
+ nil,
+ nil,
+ },
+ {
+ "ShouldNotOverrideValues",
+ nil,
+ nil,
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow, oidc.ResponseTypeImplicitFlowBoth, oidc.ResponseTypeHybridFlowBoth},
+ []string{oidc.ResponseModeFormPost},
+ []string{oidc.GrantTypeAuthorizationCode},
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow, oidc.ResponseTypeImplicitFlowBoth, oidc.ResponseTypeHybridFlowBoth},
+ []string{oidc.ResponseModeFormPost},
+ []string{oidc.GrantTypeAuthorizationCode},
+ },
+ nil,
+ nil,
+ },
+ {
+ "ShouldRaiseErrorOnDuplicateScopes",
+ nil,
+ nil,
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeOpenID},
+ nil,
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeOpenID},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery},
+ []string{oidc.GrantTypeAuthorizationCode},
+ },
+ []string{
+ "identity_providers: oidc: client 'test': option 'scopes' must have unique values but the values 'openid' are duplicated",
+ },
+ nil,
+ },
+ {
+ "ShouldRaiseErrorOnInvalidScopes",
+ nil,
+ nil,
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeProfile, "group"},
+ nil,
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeProfile, "group"},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery},
+ []string{oidc.GrantTypeAuthorizationCode},
+ },
+ nil,
+ []string{
+ "identity_providers: oidc: client 'test': option 'scopes' must only have the values 'openid', 'email', 'profile', 'groups', or 'offline_access' but the values 'group' are present",
+ },
+ },
+ {
+ "ShouldRaiseErrorOnMissingAuthorizationCodeFlowResponseTypeWithRefreshTokenValues",
+ nil,
+ nil,
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeOfflineAccess},
+ []string{oidc.ResponseTypeImplicitFlowBoth},
+ nil,
+ []string{oidc.GrantTypeImplicit, oidc.GrantTypeRefreshToken},
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeOfflineAccess},
+ []string{oidc.ResponseTypeImplicitFlowBoth},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeFragment},
+ []string{oidc.GrantTypeImplicit, oidc.GrantTypeRefreshToken},
+ },
+ []string{
+ "identity_providers: oidc: client 'test': option 'scopes' should only have the values 'offline_access' or 'offline' if the client is also configured with a 'response_type' such as 'code', 'code id_token', 'code token', or 'code id_token token' which respond with authorization codes",
+ "identity_providers: oidc: client 'test': option 'grant_types' should only have the values 'refresh_token' if the client is also configured with a 'response_type' such as 'code', 'code id_token', 'code token', or 'code id_token token' which respond with authorization codes",
+ },
+ nil,
+ },
+ {
+ "ShouldRaiseErrorOnDuplicateResponseTypes",
+ nil,
+ nil,
+ tcv{
+ nil,
+ []string{oidc.ResponseTypeAuthorizationCodeFlow, oidc.ResponseTypeImplicitFlowBoth, oidc.ResponseTypeAuthorizationCodeFlow},
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow, oidc.ResponseTypeImplicitFlowBoth, oidc.ResponseTypeAuthorizationCodeFlow},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery, oidc.ResponseModeFragment},
+ []string{oidc.GrantTypeAuthorizationCode, oidc.GrantTypeImplicit},
+ },
+ []string{
+ "identity_providers: oidc: client 'test': option 'response_types' must have unique values but the values 'code' are duplicated",
+ },
+ nil,
+ },
+ {
+ "ShouldRaiseErrorOnInvalidResponseTypesOrder",
+ nil,
+ nil,
+ tcv{
+ nil,
+ []string{oidc.ResponseTypeImplicitFlowBoth, "token id_token"},
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeImplicitFlowBoth, "token id_token"},
+ []string{"form_post", "fragment"},
+ []string{"implicit"},
+ },
+ []string{
+ "identity_providers: oidc: client 'test': option 'response_types' must only have the values 'code', 'id_token', 'token', 'id_token token', 'code id_token', 'code token', or 'code id_token token' but the values 'token id_token' are present",
+ },
+ nil,
+ },
+ {
+ "ShouldRaiseErrorOnInvalidResponseTypes",
+ nil,
+ nil,
+ tcv{
+ nil,
+ []string{"not_valid"},
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{"not_valid"},
+ []string{oidc.ResponseModeFormPost},
+ nil,
+ },
+ []string{
+ "identity_providers: oidc: client 'test': option 'response_types' must only have the values 'code', 'id_token', 'token', 'id_token token', 'code id_token', 'code token', or 'code id_token token' but the values 'not_valid' are present",
+ },
+ nil,
+ },
+ {
+ "ShouldRaiseErrorOnInvalidResponseModes",
+ nil,
+ nil,
+ tcv{
+ nil,
+ nil,
+ []string{"not_valid"},
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ []string{"not_valid"},
+ []string{oidc.GrantTypeAuthorizationCode},
+ },
+ nil,
+ []string{
+ "identity_providers: oidc: client 'test': option 'response_modes' must only have the values 'form_post', 'query', or 'fragment' but the values 'not_valid' are present",
+ },
+ },
+ {
+ "ShouldRaiseErrorOnDuplicateResponseModes",
+ nil,
+ nil,
+ tcv{
+ nil,
+ nil,
+ []string{oidc.ResponseModeQuery, oidc.ResponseModeQuery},
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ []string{oidc.ResponseModeQuery, oidc.ResponseModeQuery},
+ []string{oidc.GrantTypeAuthorizationCode},
+ },
+ []string{
+ "identity_providers: oidc: client 'test': option 'response_modes' must have unique values but the values 'query' are duplicated",
+ },
+ nil,
+ },
+ {
+ "ShouldRaiseErrorOnInvalidGrantTypes",
+ nil,
+ nil,
+ tcv{
+ nil,
+ nil,
+ nil,
+ []string{"invalid"},
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery},
+ []string{"invalid"},
+ },
+ nil,
+ []string{
+ "identity_providers: oidc: client 'test': option 'grant_types' must only have the values 'implicit', 'refresh_token', or 'authorization_code' but the values 'invalid' are present",
+ },
+ },
+ {
+ "ShouldRaiseErrorOnDuplicateGrantTypes",
+ nil,
+ nil,
+ tcv{
+ nil,
+ nil,
+ nil,
+ []string{oidc.GrantTypeAuthorizationCode, oidc.GrantTypeAuthorizationCode},
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery},
+ []string{oidc.GrantTypeAuthorizationCode, oidc.GrantTypeAuthorizationCode},
+ },
+ []string{
+ "identity_providers: oidc: client 'test': option 'grant_types' must have unique values but the values 'authorization_code' are duplicated",
+ },
+ nil,
+ },
+ {
+ "ShouldRaiseErrorOnGrantTypeRefreshTokenWithoutScopeOfflineAccess",
+ nil,
+ nil,
+ tcv{
+ nil,
+ nil,
+ nil,
+ []string{oidc.GrantTypeAuthorizationCode, oidc.GrantTypeRefreshToken},
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery},
+ []string{oidc.GrantTypeAuthorizationCode, oidc.GrantTypeRefreshToken},
+ },
+ []string{
+ "identity_providers: oidc: client 'test': option 'grant_types' should only have the 'refresh_token' value if the client is also configured with the 'offline_access' scope",
+ },
+ nil,
+ },
+ {
+ "ShouldRaiseErrorOnGrantTypeAuthorizationCodeWithoutAuthorizationCodeOrHybridFlow",
+ nil,
+ nil,
+ tcv{
+ nil,
+ []string{oidc.ResponseTypeImplicitFlowBoth},
+ nil,
+ []string{oidc.GrantTypeAuthorizationCode},
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeImplicitFlowBoth},
+ []string{"form_post", "fragment"},
+ []string{oidc.GrantTypeAuthorizationCode},
+ },
+ []string{
+ "identity_providers: oidc: client 'test': option 'grant_types' should only have grant type values which are valid with the configured 'response_types' for the client but 'authorization_code' expects a response type for either the authorization code or hybrid flow such as 'code', 'code id_token', 'code token', or 'code id_token token' but the response types are 'id_token token'",
+ },
+ nil,
+ },
+ {
+ "ShouldRaiseErrorOnGrantTypeImplicitWithoutImplicitOrHybridFlow",
+ nil,
+ nil,
+ tcv{
+ nil,
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ nil,
+ []string{oidc.GrantTypeImplicit},
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery},
+ []string{oidc.GrantTypeImplicit},
+ },
+ []string{
+ "identity_providers: oidc: client 'test': option 'grant_types' should only have grant type values which are valid with the configured 'response_types' for the client but 'implicit' expects a response type for either the implicit or hybrid flow such as 'id_token', 'token', 'id_token token', 'code id_token', 'code token', or 'code id_token token' but the response types are 'code'",
+ },
+ nil,
+ },
+ {
+ "ShouldValidateCorrectRedirectURIsConfidentialClientType",
+ func(have *schema.OpenIDConnectConfiguration) {
+ have.Clients[0].RedirectURIs = []string{
+ "https://google.com",
+ }
+ },
+ func(t *testing.T, have *schema.OpenIDConnectConfiguration) {
+ assert.Equal(t, []string{"https://google.com"}, have.Clients[0].RedirectURIs)
+ },
+ tcv{
+ nil,
+ nil,
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery},
+ []string{oidc.GrantTypeAuthorizationCode},
+ },
+ nil,
+ nil,
+ },
+ {
+ "ShouldValidateCorrectRedirectURIsPublicClientType",
+ func(have *schema.OpenIDConnectConfiguration) {
+ have.Clients[0].Public = true
+ have.Clients[0].Secret = nil
+ have.Clients[0].RedirectURIs = []string{
+ oauth2InstalledApp,
+ }
+ },
+ func(t *testing.T, have *schema.OpenIDConnectConfiguration) {
+ assert.Equal(t, []string{oauth2InstalledApp}, have.Clients[0].RedirectURIs)
+ },
+ tcv{
+ nil,
+ nil,
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery},
+ []string{oidc.GrantTypeAuthorizationCode},
+ },
+ nil,
+ nil,
+ },
+ {
+ "ShouldRaiseErrorOnInvalidRedirectURIsPublicOnly",
+ func(have *schema.OpenIDConnectConfiguration) {
+ have.Clients[0].RedirectURIs = []string{
+ "urn:ietf:wg:oauth:2.0:oob",
+ }
+ },
+ func(t *testing.T, have *schema.OpenIDConnectConfiguration) {
+ assert.Equal(t, []string{oauth2InstalledApp}, have.Clients[0].RedirectURIs)
+ },
+ tcv{
+ nil,
+ nil,
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery},
+ []string{oidc.GrantTypeAuthorizationCode},
+ },
+ nil,
+ []string{
+ "identity_providers: oidc: client 'test': option 'redirect_uris' has the redirect uri 'urn:ietf:wg:oauth:2.0:oob' when option 'public' is false but this is invalid as this uri is not valid for the openid connect confidential client type",
+ },
+ },
+ {
+ "ShouldRaiseErrorOnInvalidRedirectURIsMalformedURI",
+ func(have *schema.OpenIDConnectConfiguration) {
+ have.Clients[0].RedirectURIs = []string{
+ "http://abc@%two",
+ }
+ },
+ func(t *testing.T, have *schema.OpenIDConnectConfiguration) {
+ assert.Equal(t, []string{"http://abc@%two"}, have.Clients[0].RedirectURIs)
+ },
+ tcv{
+ nil,
+ nil,
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery},
+ []string{oidc.GrantTypeAuthorizationCode},
+ },
+ nil,
+ []string{
+ "identity_providers: oidc: client 'test': option 'redirect_uris' has an invalid value: redirect uri 'http://abc@%two' could not be parsed: parse \"http://abc@%two\": invalid URL escape \"%tw\"",
+ },
+ },
+ {
+ "ShouldRaiseErrorOnInvalidRedirectURIsNotAbsolute",
+ func(have *schema.OpenIDConnectConfiguration) {
+ have.Clients[0].RedirectURIs = []string{
+ "google.com",
+ }
+ },
+ func(t *testing.T, have *schema.OpenIDConnectConfiguration) {
+ assert.Equal(t, []string{"google.com"}, have.Clients[0].RedirectURIs)
+ },
+ tcv{
+ nil,
+ nil,
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery},
+ []string{oidc.GrantTypeAuthorizationCode},
+ },
+ nil,
+ []string{
+ "identity_providers: oidc: client 'test': option 'redirect_uris' has an invalid value: redirect uri 'google.com' must have a scheme but it's absent",
+ },
+ },
+ {
+ "ShouldRaiseErrorOnDuplicateRedirectURI",
+ func(have *schema.OpenIDConnectConfiguration) {
+ have.Clients[0].RedirectURIs = []string{
+ "https://google.com",
+ "https://google.com",
+ }
+ },
+ func(t *testing.T, have *schema.OpenIDConnectConfiguration) {
+ assert.Equal(t, []string{"https://google.com", "https://google.com"}, have.Clients[0].RedirectURIs)
+ },
+ tcv{
+ nil,
+ nil,
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery},
+ []string{oidc.GrantTypeAuthorizationCode},
+ },
+ []string{
+ "identity_providers: oidc: client 'test': option 'redirect_uris' must have unique values but the values 'https://google.com' are duplicated",
+ },
+ nil,
+ },
+ {
+ "ShouldNotSetDefaultTokenEndpointClientAuthMethodConfidentialClientType",
+ nil,
+ func(t *testing.T, have *schema.OpenIDConnectConfiguration) {
+ assert.Equal(t, "", have.Clients[0].TokenEndpointAuthMethod)
+ },
+ tcv{
+ nil,
+ nil,
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery},
+ []string{oidc.GrantTypeAuthorizationCode},
+ },
+ nil,
+ nil,
+ },
+ {
+ "ShouldSetDefaultTokenEndpointClientAuthMethodPublicClientType",
+ func(have *schema.OpenIDConnectConfiguration) {
+ have.Clients[0].Public = true
+ have.Clients[0].Secret = nil
+ },
+ func(t *testing.T, have *schema.OpenIDConnectConfiguration) {
+ assert.Equal(t, oidc.ClientAuthMethodNone, have.Clients[0].TokenEndpointAuthMethod)
+ },
+ tcv{
+ nil,
+ nil,
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery},
+ []string{oidc.GrantTypeAuthorizationCode},
+ },
+ nil,
+ nil,
+ },
+ {
+ "ShouldSetDefaultTokenEndpointClientAuthMethodConfidentialClientTypeImplicitFlow",
+ nil,
+ func(t *testing.T, have *schema.OpenIDConnectConfiguration) {
+ assert.Equal(t, oidc.ClientAuthMethodNone, have.Clients[0].TokenEndpointAuthMethod)
+ },
+ tcv{
+ nil,
+ []string{oidc.ResponseTypeImplicitFlowIDToken, oidc.ResponseTypeImplicitFlowToken, oidc.ResponseTypeImplicitFlowBoth},
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeImplicitFlowIDToken, oidc.ResponseTypeImplicitFlowToken, oidc.ResponseTypeImplicitFlowBoth},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeFragment},
+ []string{oidc.GrantTypeImplicit},
+ },
+ nil,
+ nil,
+ },
+ {
+ "ShouldNotOverrideValidClientAuthMethod",
+ func(have *schema.OpenIDConnectConfiguration) {
+ have.Clients[0].TokenEndpointAuthMethod = oidc.ClientAuthMethodClientSecretPost
+ },
+ func(t *testing.T, have *schema.OpenIDConnectConfiguration) {
+ assert.Equal(t, oidc.ClientAuthMethodClientSecretPost, have.Clients[0].TokenEndpointAuthMethod)
+ },
+ tcv{
+ nil,
+ nil,
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery},
+ []string{oidc.GrantTypeAuthorizationCode},
+ },
+ nil,
+ nil,
+ },
+ {
+ "ShouldRaiseErrorOnInvalidClientAuthMethod",
+ func(have *schema.OpenIDConnectConfiguration) {
+ have.Clients[0].TokenEndpointAuthMethod = "client_credentials"
+ },
+ func(t *testing.T, have *schema.OpenIDConnectConfiguration) {
+ assert.Equal(t, "client_credentials", have.Clients[0].TokenEndpointAuthMethod)
+ },
+ tcv{
+ nil,
+ nil,
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery},
+ []string{oidc.GrantTypeAuthorizationCode},
+ },
+ nil,
+ []string{
+ "identity_providers: oidc: client 'test': option 'token_endpoint_auth_method' must be one of 'none', 'client_secret_post', or 'client_secret_basic' but it's configured as 'client_credentials'",
+ },
+ },
+ {
+ "ShouldRaiseErrorOnInvalidClientAuthMethodForPublicClientType",
+ func(have *schema.OpenIDConnectConfiguration) {
+ have.Clients[0].TokenEndpointAuthMethod = oidc.ClientAuthMethodClientSecretBasic
+ have.Clients[0].Public = true
+ have.Clients[0].Secret = nil
+ },
+ func(t *testing.T, have *schema.OpenIDConnectConfiguration) {
+ assert.Equal(t, oidc.ClientAuthMethodClientSecretBasic, have.Clients[0].TokenEndpointAuthMethod)
+ },
+ tcv{
+ nil,
+ nil,
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery},
+ []string{oidc.GrantTypeAuthorizationCode},
+ },
+ nil,
+ []string{
+ "identity_providers: oidc: client 'test': option 'token_endpoint_auth_method' must be 'none' when configured as the public client type but it's configured as 'client_secret_basic'",
+ },
+ },
+ {
+ "ShouldRaiseErrorOnInvalidClientAuthMethodForConfidentialClientTypeAuthorizationCodeFlow",
+ func(have *schema.OpenIDConnectConfiguration) {
+ have.Clients[0].TokenEndpointAuthMethod = oidc.ClientAuthMethodNone
+ },
+ func(t *testing.T, have *schema.OpenIDConnectConfiguration) {
+ assert.Equal(t, oidc.ClientAuthMethodNone, have.Clients[0].TokenEndpointAuthMethod)
+ },
+ tcv{
+ nil,
+ nil,
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery},
+ []string{oidc.GrantTypeAuthorizationCode},
+ },
+ nil,
+ []string{
+ "identity_providers: oidc: client 'test': option 'token_endpoint_auth_method' must be one of 'client_secret_post' or 'client_secret_basic' when configured as the confidential client type unless it only includes implicit flow response types such as 'id_token', 'token', and 'id_token token' but it's configured as 'none'",
+ },
+ },
+ {
+ "ShouldRaiseErrorOnInvalidClientAuthMethodForConfidentialClientTypeHybridFlow",
+ func(have *schema.OpenIDConnectConfiguration) {
+ have.Clients[0].TokenEndpointAuthMethod = oidc.ClientAuthMethodNone
+ },
+ func(t *testing.T, have *schema.OpenIDConnectConfiguration) {
+ assert.Equal(t, oidc.ClientAuthMethodNone, have.Clients[0].TokenEndpointAuthMethod)
+ },
+ tcv{
+ nil,
+ []string{oidc.ResponseTypeHybridFlowToken},
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeHybridFlowToken},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeFragment},
+ []string{oidc.GrantTypeAuthorizationCode, oidc.GrantTypeImplicit},
+ },
+ nil,
+ []string{
+ "identity_providers: oidc: client 'test': option 'token_endpoint_auth_method' must be one of 'client_secret_post' or 'client_secret_basic' when configured as the confidential client type unless it only includes implicit flow response types such as 'id_token', 'token', and 'id_token token' but it's configured as 'none'",
+ },
+ },
+ {
+ "ShouldSetDefaultUserInfoAlg",
+ nil,
+ func(t *testing.T, have *schema.OpenIDConnectConfiguration) {
+ assert.Equal(t, oidc.SigningAlgorithmNone, have.Clients[0].UserinfoSigningAlgorithm)
+ },
+ tcv{
+ nil,
+ nil,
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery},
+ []string{oidc.GrantTypeAuthorizationCode},
+ },
+ nil,
+ nil,
+ },
+ {
+ "ShouldNotOverrideUserInfoAlg",
+ func(have *schema.OpenIDConnectConfiguration) {
+ have.Clients[0].UserinfoSigningAlgorithm = oidc.SigningAlgorithmRSAWithSHA256
+ },
+ func(t *testing.T, have *schema.OpenIDConnectConfiguration) {
+ assert.Equal(t, oidc.SigningAlgorithmRSAWithSHA256, have.Clients[0].UserinfoSigningAlgorithm)
+ },
+ tcv{
+ nil,
+ nil,
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery},
+ []string{oidc.GrantTypeAuthorizationCode},
+ },
+ nil,
+ nil,
+ },
+ {
+ "ShouldRaiseErrorOnInvalidUserInfoAlg",
+ func(have *schema.OpenIDConnectConfiguration) {
+ have.Clients[0].UserinfoSigningAlgorithm = "rs256"
+ },
+ func(t *testing.T, have *schema.OpenIDConnectConfiguration) {
+ assert.Equal(t, "rs256", have.Clients[0].UserinfoSigningAlgorithm)
+ },
+ tcv{
+ nil,
+ nil,
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery},
+ []string{oidc.GrantTypeAuthorizationCode},
+ },
+ nil,
+ []string{
+ "identity_providers: oidc: client 'test': option 'userinfo_signing_algorithm' must be one of 'none' or 'RS256' but it's configured as 'rs256'",
+ },
+ },
+ {
+ "ShouldSetDefaultConsentMode",
+ nil,
+ func(t *testing.T, have *schema.OpenIDConnectConfiguration) {
+ assert.Equal(t, "explicit", have.Clients[0].ConsentMode)
+ },
+ tcv{
+ nil,
+ nil,
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery},
+ []string{oidc.GrantTypeAuthorizationCode},
+ },
+ nil,
+ nil,
+ },
+ {
+ "ShouldSetDefaultConsentModeAuto",
+ func(have *schema.OpenIDConnectConfiguration) {
+ have.Clients[0].ConsentMode = auto
+ },
+ func(t *testing.T, have *schema.OpenIDConnectConfiguration) {
+ assert.Equal(t, "explicit", have.Clients[0].ConsentMode)
+ },
+ tcv{
+ nil,
+ nil,
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery},
+ []string{oidc.GrantTypeAuthorizationCode},
+ },
+ nil,
+ nil,
+ },
+ {
+ "ShouldSetDefaultConsentModePreConfigured",
+ func(have *schema.OpenIDConnectConfiguration) {
+ d := time.Minute
+
+ have.Clients[0].ConsentMode = ""
+ have.Clients[0].ConsentPreConfiguredDuration = &d
+ },
+ func(t *testing.T, have *schema.OpenIDConnectConfiguration) {
+ assert.Equal(t, "pre-configured", have.Clients[0].ConsentMode)
+ },
+ tcv{
+ nil,
+ nil,
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery},
+ []string{oidc.GrantTypeAuthorizationCode},
+ },
+ nil,
+ nil,
+ },
+ {
+ "ShouldSetDefaultConsentModeAutoPreConfigured",
+ func(have *schema.OpenIDConnectConfiguration) {
+ d := time.Minute
+
+ have.Clients[0].ConsentMode = auto
+ have.Clients[0].ConsentPreConfiguredDuration = &d
+ },
+ func(t *testing.T, have *schema.OpenIDConnectConfiguration) {
+ assert.Equal(t, "pre-configured", have.Clients[0].ConsentMode)
+ },
+ tcv{
+ nil,
+ nil,
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery},
+ []string{oidc.GrantTypeAuthorizationCode},
+ },
+ nil,
+ nil,
+ },
+ {
+ "ShouldNotOverrideConsentMode",
+ func(have *schema.OpenIDConnectConfiguration) {
+ have.Clients[0].ConsentMode = "implicit"
+ },
+ func(t *testing.T, have *schema.OpenIDConnectConfiguration) {
+ assert.Equal(t, "implicit", have.Clients[0].ConsentMode)
+ },
+ tcv{
+ nil,
+ nil,
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery},
+ []string{oidc.GrantTypeAuthorizationCode},
+ },
+ nil,
+ nil,
+ },
+ {
+ "ShouldSentConsentPreConfiguredDefaultDuration",
+ func(have *schema.OpenIDConnectConfiguration) {
+ have.Clients[0].ConsentMode = "pre-configured"
+ },
+ func(t *testing.T, have *schema.OpenIDConnectConfiguration) {
+ assert.Equal(t, "pre-configured", have.Clients[0].ConsentMode)
+ assert.Equal(t, schema.DefaultOpenIDConnectClientConfiguration.ConsentPreConfiguredDuration, have.Clients[0].ConsentPreConfiguredDuration)
+ },
+ tcv{
+ nil,
+ nil,
+ nil,
+ nil,
+ },
+ tcv{
+ []string{oidc.ScopeOpenID, oidc.ScopeGroups, oidc.ScopeProfile, oidc.ScopeEmail},
+ []string{oidc.ResponseTypeAuthorizationCodeFlow},
+ []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery},
+ []string{oidc.GrantTypeAuthorizationCode},
+ },
+ nil,
+ nil,
+ },
+ }
+
+ errDeprecatedFunc := func() {}
+
+ for _, tc := range testCasses {
+ t.Run(tc.name, func(t *testing.T) {
+ have := &schema.OpenIDConnectConfiguration{
+ Clients: []schema.OpenIDConnectClientConfiguration{
+ {
+ ID: "test",
+ Secret: MustDecodeSecret("$pbkdf2-sha512$310000$c8p78n7pUMln0jzvd4aK4Q$JNRBzwAo0ek5qKn50cFzzvE9RXV88h1wJn5KGiHrD0YKtZaR/nCb2CJPOsKaPK0hjf.9yHxzQGZziziccp6Yng"),
+ Scopes: tc.have.Scopes,
+ ResponseModes: tc.have.ResponseModes,
+ ResponseTypes: tc.have.ResponseTypes,
+ GrantTypes: tc.have.GrantTypes,
+ },
+ },
+ }
+
+ if tc.setup != nil {
+ tc.setup(have)
+ }
+
+ val := schema.NewStructValidator()
+
+ validateOIDCClient(0, have, val, errDeprecatedFunc)
+
+ t.Run("General", func(t *testing.T) {
+ assert.Equal(t, tc.expected.Scopes, have.Clients[0].Scopes)
+ assert.Equal(t, tc.expected.ResponseTypes, have.Clients[0].ResponseTypes)
+ assert.Equal(t, tc.expected.ResponseModes, have.Clients[0].ResponseModes)
+ assert.Equal(t, tc.expected.GrantTypes, have.Clients[0].GrantTypes)
+
+ if tc.validate != nil {
+ tc.validate(t, have)
+ }
+ })
+
+ t.Run("Warnings", func(t *testing.T) {
+ require.Len(t, val.Warnings(), len(tc.serrs))
+ for i, err := range tc.serrs {
+ assert.EqualError(t, val.Warnings()[i], err)
+ }
+ })
+
+ t.Run("Errors", func(t *testing.T) {
+ require.Len(t, val.Errors(), len(tc.errs))
+ for i, err := range tc.errs {
+ assert.EqualError(t, val.Errors()[i], err)
+ }
+ })
+ })
+ }
+}
+
+func TestValidateOIDCClientTokenEndpointAuthMethod(t *testing.T) {
+ testCasses := []struct {
+ name string
+ have string
+ public bool
+ expected string
+ errs []string
+ }{
+ {"ShouldSetDefaultValueConfidential", "", false, "", nil},
+ {"ShouldSetDefaultValuePublic", "", true, oidc.ClientAuthMethodNone, nil},
+ {"ShouldErrorOnInvalidValue", "abc", false, "abc",
+ []string{
+ "identity_providers: oidc: client 'test': option 'token_endpoint_auth_method' must be one of 'none', 'client_secret_post', or 'client_secret_basic' but it's configured as 'abc'",
+ },
+ },
+ {"ShouldErrorOnInvalidValueForPublicClient", "client_secret_post", true, "client_secret_post",
+ []string{
+ "identity_providers: oidc: client 'test': option 'token_endpoint_auth_method' must be 'none' when configured as the public client type but it's configured as 'client_secret_post'",
+ },
+ },
+ {"ShouldErrorOnInvalidValueForConfidentialClient", "none", false, "none",
+ []string{
+ "identity_providers: oidc: client 'test': option 'token_endpoint_auth_method' must be one of 'client_secret_post' or 'client_secret_basic' when configured as the confidential client type unless it only includes implicit flow response types such as 'id_token', 'token', and 'id_token token' but it's configured as 'none'",
+ },
+ },
+ }
+
+ for _, tc := range testCasses {
+ t.Run(tc.name, func(t *testing.T) {
+ have := &schema.OpenIDConnectConfiguration{
+ Clients: []schema.OpenIDConnectClientConfiguration{
+ {
+ ID: "test",
+ Public: tc.public,
+ TokenEndpointAuthMethod: tc.have,
+ },
+ },
+ }
+
+ val := schema.NewStructValidator()
+
+ validateOIDCClientTokenEndpointAuthMethod(0, have, val)
+
+ assert.Equal(t, tc.expected, have.Clients[0].TokenEndpointAuthMethod)
+ assert.Len(t, val.Warnings(), 0)
+ require.Len(t, val.Errors(), len(tc.errs))
+
+ if tc.errs != nil {
+ for i, err := range tc.errs {
+ assert.EqualError(t, val.Errors()[i], err)
+ }
+ }
+ })
+ }
+}
+
func MustDecodeSecret(value string) *schema.PasswordDigest {
if secret, err := schema.DecodePasswordDigest(value); err != nil {
panic(err)