diff --git a/conf/defaults.ini b/conf/defaults.ini index 85d0953c6af..15b8927e65a 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -321,6 +321,7 @@ allow_sign_up = true client_id = some_id client_secret = some_secret scopes = user:email +email_attribute_name = email:primary auth_url = token_url = api_url = diff --git a/docs/sources/auth/generic-oauth.md b/docs/sources/auth/generic-oauth.md index bec5a98e04a..802424f180b 100644 --- a/docs/sources/auth/generic-oauth.md +++ b/docs/sources/auth/generic-oauth.md @@ -32,7 +32,14 @@ allowed_domains = mycompany.com mycompany.org allow_sign_up = true ``` -Set api_url to the resource that returns [OpenID UserInfo](https://connect2id.com/products/server/docs/api/userinfo) compatible information. +Set `api_url` to the resource that returns [OpenID UserInfo](https://connect2id.com/products/server/docs/api/userinfo) compatible information. + +Grafana will attempt to determine the user's e-mail address by querying the OAuth provider as described below in the following order until an e-mail address is found: + +1. Check for the presence of an e-mail address via the `email` field encoded in the OAuth `id_token` parameter. +2. Check for the presence of an e-mail address in the `attributes` map encoded in the OAuth `id_token` parameter. By default Grafana will perform a lookup into the attributes map using the `email:primary` key, however, this is configurable and can be adjusted by using the `email_attribute_name` configuration option. +3. Query the `/emails` endpoint of the OAuth provider's API (configured with `api_url`) and check for the presence of an e-mail address marked as a primary address. +4. If no e-mail address is found in steps (1-3), then the e-mail address of the user is set to the empty string. ## Set up OAuth2 with Okta diff --git a/pkg/setting/setting_oauth.go b/pkg/setting/setting_oauth.go index ee2e812415b..93b1ab6f101 100644 --- a/pkg/setting/setting_oauth.go +++ b/pkg/setting/setting_oauth.go @@ -5,6 +5,7 @@ type OAuthInfo struct { Scopes []string AuthUrl, TokenUrl string Enabled bool + EmailAttributeName string AllowedDomains []string HostedDomain string ApiUrl string diff --git a/pkg/social/generic_oauth.go b/pkg/social/generic_oauth.go index 8c02076096d..a97d58334c7 100644 --- a/pkg/social/generic_oauth.go +++ b/pkg/social/generic_oauth.go @@ -20,6 +20,7 @@ type SocialGenericOAuth struct { allowedOrganizations []string apiUrl string allowSignup bool + emailAttributeName string teamIds []int } @@ -264,8 +265,9 @@ func (s *SocialGenericOAuth) extractEmail(data *UserInfoJson) string { return data.Email } - if data.Attributes["email:primary"] != nil { - return data.Attributes["email:primary"][0] + emails, ok := data.Attributes[s.emailAttributeName] + if ok && len(emails) != 0 { + return emails[0] } if data.Upn != "" { diff --git a/pkg/social/social.go b/pkg/social/social.go index 2be71514629..e96b67fe031 100644 --- a/pkg/social/social.go +++ b/pkg/social/social.go @@ -60,21 +60,22 @@ func NewOAuthService() { for _, name := range allOauthes { sec := setting.Raw.Section("auth." + name) info := &setting.OAuthInfo{ - ClientId: sec.Key("client_id").String(), - ClientSecret: sec.Key("client_secret").String(), - Scopes: util.SplitString(sec.Key("scopes").String()), - AuthUrl: sec.Key("auth_url").String(), - TokenUrl: sec.Key("token_url").String(), - ApiUrl: sec.Key("api_url").String(), - Enabled: sec.Key("enabled").MustBool(), - AllowedDomains: util.SplitString(sec.Key("allowed_domains").String()), - HostedDomain: sec.Key("hosted_domain").String(), - AllowSignup: sec.Key("allow_sign_up").MustBool(), - Name: sec.Key("name").MustString(name), - TlsClientCert: sec.Key("tls_client_cert").String(), - TlsClientKey: sec.Key("tls_client_key").String(), - TlsClientCa: sec.Key("tls_client_ca").String(), - TlsSkipVerify: sec.Key("tls_skip_verify_insecure").MustBool(), + ClientId: sec.Key("client_id").String(), + ClientSecret: sec.Key("client_secret").String(), + Scopes: util.SplitString(sec.Key("scopes").String()), + AuthUrl: sec.Key("auth_url").String(), + TokenUrl: sec.Key("token_url").String(), + ApiUrl: sec.Key("api_url").String(), + Enabled: sec.Key("enabled").MustBool(), + EmailAttributeName: sec.Key("email_attribute_name").String(), + AllowedDomains: util.SplitString(sec.Key("allowed_domains").String()), + HostedDomain: sec.Key("hosted_domain").String(), + AllowSignup: sec.Key("allow_sign_up").MustBool(), + Name: sec.Key("name").MustString(name), + TlsClientCert: sec.Key("tls_client_cert").String(), + TlsClientKey: sec.Key("tls_client_key").String(), + TlsClientCa: sec.Key("tls_client_ca").String(), + TlsSkipVerify: sec.Key("tls_skip_verify_insecure").MustBool(), } if !info.Enabled { @@ -153,6 +154,7 @@ func NewOAuthService() { allowedDomains: info.AllowedDomains, apiUrl: info.ApiUrl, allowSignup: info.AllowSignup, + emailAttributeName: info.EmailAttributeName, teamIds: sec.Key("team_ids").Ints(","), allowedOrganizations: util.SplitString(sec.Key("allowed_organizations").String()), }