diff --git a/connector/oauth/oauth.go b/connector/oauth/oauth.go index 413a813a08..d43e25373e 100644 --- a/connector/oauth/oauth.go +++ b/connector/oauth/oauth.go @@ -17,21 +17,22 @@ import ( ) type oauthConnector struct { - clientID string - clientSecret string - redirectURI string - tokenURL string - authorizationURL string - userInfoURL string - scopes []string - userIDKey string - userNameKey string - preferredUsernameKey string - emailKey string - emailVerifiedKey string - groupsKey string - httpClient *http.Client - logger *slog.Logger + clientID string + clientSecret string + redirectURI string + tokenURL string + authorizationURL string + userInfoURL string + scopes []string + userIDKey string + userNameKey string + preferredUsernameKey string + emailKey string + emailVerifiedKey string + groupsKey string + insecureSkipEmailVerified bool + httpClient *http.Client + logger *slog.Logger } type connectorData struct { @@ -39,17 +40,19 @@ type connectorData struct { } type Config struct { - ClientID string `json:"clientID"` - ClientSecret string `json:"clientSecret"` - RedirectURI string `json:"redirectURI"` - TokenURL string `json:"tokenURL"` - AuthorizationURL string `json:"authorizationURL"` - UserInfoURL string `json:"userInfoURL"` - Scopes []string `json:"scopes"` - RootCAs []string `json:"rootCAs"` - InsecureSkipVerify bool `json:"insecureSkipVerify"` - UserIDKey string `json:"userIDKey"` // defaults to "id" - ClaimMapping struct { + ClientID string `json:"clientID"` + ClientSecret string `json:"clientSecret"` + RedirectURI string `json:"redirectURI"` + TokenURL string `json:"tokenURL"` + AuthorizationURL string `json:"authorizationURL"` + UserInfoURL string `json:"userInfoURL"` + Scopes []string `json:"scopes"` + RootCAs []string `json:"rootCAs"` + // Override the value of email_verified to true in the returned claims + InsecureSkipEmailVerified bool `json:"insecureSkipEmailVerified"` + InsecureSkipVerify bool `json:"insecureSkipVerify"` + UserIDKey string `json:"userIDKey"` // defaults to "id" + ClaimMapping struct { UserNameKey string `json:"userNameKey"` // defaults to "user_name" PreferredUsernameKey string `json:"preferredUsernameKey"` // defaults to "preferred_username" GroupsKey string `json:"groupsKey"` // defaults to "groups" @@ -92,20 +95,21 @@ func (c *Config) Open(id string, logger *slog.Logger) (connector.Connector, erro } oauthConn := &oauthConnector{ - clientID: c.ClientID, - clientSecret: c.ClientSecret, - tokenURL: c.TokenURL, - authorizationURL: c.AuthorizationURL, - userInfoURL: c.UserInfoURL, - scopes: c.Scopes, - redirectURI: c.RedirectURI, - logger: logger.With(slog.Group("connector", "type", "oauth", "id", id)), - userIDKey: userIDKey, - userNameKey: userNameKey, - preferredUsernameKey: preferredUsernameKey, - groupsKey: groupsKey, - emailKey: emailKey, - emailVerifiedKey: emailVerifiedKey, + clientID: c.ClientID, + clientSecret: c.ClientSecret, + tokenURL: c.TokenURL, + authorizationURL: c.AuthorizationURL, + userInfoURL: c.UserInfoURL, + scopes: c.Scopes, + redirectURI: c.RedirectURI, + logger: logger.With(slog.Group("connector", "type", "oauth", "id", id)), + userIDKey: userIDKey, + userNameKey: userNameKey, + preferredUsernameKey: preferredUsernameKey, + groupsKey: groupsKey, + emailKey: emailKey, + emailVerifiedKey: emailVerifiedKey, + insecureSkipEmailVerified: c.InsecureSkipEmailVerified, } oauthConn.httpClient, err = httpclient.NewHTTPClient(c.RootCAs, c.InsecureSkipVerify) @@ -186,7 +190,14 @@ func (c *oauthConnector) HandleCallback(s connector.Scopes, r *http.Request) (id identity.Username, _ = userInfoResult[c.userNameKey].(string) identity.PreferredUsername, _ = userInfoResult[c.preferredUsernameKey].(string) identity.Email, _ = userInfoResult[c.emailKey].(string) - identity.EmailVerified, _ = userInfoResult[c.emailVerifiedKey].(bool) + + identity.EmailVerified, found = userInfoResult[c.emailVerifiedKey].(bool) + + if !found { + if c.insecureSkipEmailVerified { + identity.EmailVerified = true + } + } if s.Groups { groups := map[string]struct{}{} diff --git a/connector/oauth/oauth_test.go b/connector/oauth/oauth_test.go index d06c0c0840..f1042ead2d 100644 --- a/connector/oauth/oauth_test.go +++ b/connector/oauth/oauth_test.go @@ -197,6 +197,65 @@ func TestHandleCallbackForNumericUserID(t *testing.T) { assert.Equal(t, identity.EmailVerified, false) } +func TestHandleCallbackWithInsecureSkipEmailVerifiedWhenKeyFound(t *testing.T) { + tokenClaims := map[string]interface{}{} + + userInfoClaims := map[string]interface{}{ + "name": "test-name", + "user_id_key": 1000, + "user_name_key": "test-username", + "preferred_username": "test-preferred-username", + "mail": "mod_mail", + "has_verified_email": false, + } + + testServer := testSetup(t, tokenClaims, userInfoClaims) + defer testServer.Close() + + conn := newConnector(t, testServer.URL) + conn.insecureSkipEmailVerified = true + + req := newRequestWithAuthCode(t, testServer.URL, "TestHandleCallbackWithInsecureSkipEmailVerifiedWhenKeyFound") + + identity, err := conn.HandleCallback(connector.Scopes{Groups: true}, req) + assert.Equal(t, err, nil) + + assert.Equal(t, identity.UserID, "1000") + assert.Equal(t, identity.Username, "test-username") + assert.Equal(t, identity.PreferredUsername, "test-preferred-username") + assert.Equal(t, identity.Email, "mod_mail") + assert.Equal(t, identity.EmailVerified, false) +} + +func TestHandleCallbackWithInsecureSkipEmailVerifiedWhenKeyNotFound(t *testing.T) { + tokenClaims := map[string]interface{}{} + + userInfoClaims := map[string]interface{}{ + "name": "test-name", + "user_id_key": 1000, + "user_name_key": "test-username", + "preferred_username": "test-preferred-username", + "mail": "mod_mail", + } + + testServer := testSetup(t, tokenClaims, userInfoClaims) + defer testServer.Close() + + conn := newConnector(t, testServer.URL) + conn.insecureSkipEmailVerified = true + + req := newRequestWithAuthCode(t, testServer.URL, "TestHandleCallbackWithInsecureSkipEmailVerifiedWhenKeyNotFound") + + identity, err := conn.HandleCallback(connector.Scopes{Groups: true}, req) + assert.Equal(t, err, nil) + + assert.Equal(t, identity.UserID, "1000") + assert.Equal(t, identity.Username, "test-username") + assert.Equal(t, identity.PreferredUsername, "test-preferred-username") + assert.Equal(t, identity.Email, "mod_mail") + assert.Equal(t, identity.EmailVerified, true) +} + func testSetup(t *testing.T, tokenClaims map[string]interface{}, userInfoClaims map[string]interface{}) *httptest.Server { key, err := rsa.GenerateKey(rand.Reader, 1024) if err != nil {