diff --git a/Documentation/connectors/oidc.md b/Documentation/connectors/oidc.md index c472e303..4df28915 100644 --- a/Documentation/connectors/oidc.md +++ b/Documentation/connectors/oidc.md @@ -56,6 +56,11 @@ connectors: # - email # - groups + # Some providers return no standard email claim key (ex: 'mail') + # Override email claim key + # Default is "email" + # emailClaim: email + # Some providers return claims without "email_verified", when they had no usage of emails verification in enrollment process # or if they are acting as a proxy for another IDP etc AWS Cognito with an upstream SAML IDP # This can be overridden with the below option diff --git a/connector/oidc/oidc.go b/connector/oidc/oidc.go index c8172811..d1faba7b 100644 --- a/connector/oidc/oidc.go +++ b/connector/oidc/oidc.go @@ -58,6 +58,9 @@ type Config struct { // Configurable key which contains the preferred username claims PreferredUsernameKey string `json:"preferredUsernameKey"` + // EmailClaim override email claim key. Defaults to "email" + EmailClaim string `json:"emailClaim"` + // PromptType will be used fot the prompt parameter (when offline_access, by default prompt=consent) PromptType string `json:"promptType"` } @@ -112,6 +115,11 @@ func (c *Config) Open(id string, logger log.Logger) (conn connector.Connector, e endpoint.AuthStyle = oauth2.AuthStyleInParams } + emailClaim := "email" + if len(c.EmailClaim) > 0 { + emailClaim = c.EmailClaim + } + scopes := []string{oidc.ScopeOpenID} if len(c.Scopes) > 0 { scopes = append(scopes, c.Scopes...) @@ -147,6 +155,7 @@ func (c *Config) Open(id string, logger log.Logger) (conn connector.Connector, e userIDKey: c.UserIDKey, userNameKey: c.UserNameKey, preferredUsernameKey: c.PreferredUsernameKey, + emailClaim: emailClaim, promptType: c.PromptType, }, nil } @@ -170,6 +179,7 @@ type oidcConnector struct { userIDKey string userNameKey string preferredUsernameKey string + emailClaim string promptType string } @@ -286,9 +296,9 @@ func (c *oidcConnector) createIdentity(ctx context.Context, identity connector.I } } - email, found := claims["email"].(string) + email, found := claims[c.emailClaim].(string) if !found && hasEmailScope { - return identity, errors.New("missing \"email\" claim") + return identity, fmt.Errorf("missing \"%s\" claim", c.emailClaim) } emailVerified, found := claims["email_verified"].(bool) diff --git a/connector/oidc/oidc_test.go b/connector/oidc/oidc_test.go index b5c717b9..de66db93 100644 --- a/connector/oidc/oidc_test.go +++ b/connector/oidc/oidc_test.go @@ -52,6 +52,7 @@ func TestHandleCallback(t *testing.T) { preferredUsernameKey string insecureSkipEmailVerified bool scopes []string + emailClaim string expectUserID string expectUserName string expectPreferredUsername string @@ -72,6 +73,21 @@ func TestHandleCallback(t *testing.T) { "email_verified": true, }, }, + { + name: "customEmailClaim", + userIDKey: "", // not configured + userNameKey: "", // not configured + emailClaim: "mail", + expectUserID: "subvalue", + expectUserName: "namevalue", + expectedEmailField: "emailvalue", + token: map[string]interface{}{ + "sub": "subvalue", + "name": "namevalue", + "mail": "emailvalue", + "email_verified": true, + }, + }, { name: "email_verified not in claims, configured to be skipped", insecureSkipEmailVerified: true, @@ -206,6 +222,7 @@ func TestHandleCallback(t *testing.T) { UserIDKey: tc.userIDKey, UserNameKey: tc.userNameKey, PreferredUsernameKey: tc.preferredUsernameKey, + EmailClaim: tc.emailClaim, InsecureSkipEmailVerified: tc.insecureSkipEmailVerified, BasicAuthUnsupported: &basicAuth, }