Commit f257ff02 by Bob Shannon Committed by Torkel Ödegaard

Allow oauth email attribute name to be configurable (#13006)

* Allow oauth email attribute name to be configurable

Signed-off-by: Bob Shannon <bshannon@palantir.com>

* Document e-mail determination steps for generic oauth

* Add reference to email_attribute_name

* Re-add e-mail determination docs to new generic-oauth page

* Inherit default e-mail attribute from defaults.ini
parent 7c78b64a
...@@ -321,6 +321,7 @@ allow_sign_up = true ...@@ -321,6 +321,7 @@ allow_sign_up = true
client_id = some_id client_id = some_id
client_secret = some_secret client_secret = some_secret
scopes = user:email scopes = user:email
email_attribute_name = email:primary
auth_url = auth_url =
token_url = token_url =
api_url = api_url =
......
...@@ -32,7 +32,14 @@ allowed_domains = mycompany.com mycompany.org ...@@ -32,7 +32,14 @@ allowed_domains = mycompany.com mycompany.org
allow_sign_up = true 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 ## Set up OAuth2 with Okta
......
...@@ -5,6 +5,7 @@ type OAuthInfo struct { ...@@ -5,6 +5,7 @@ type OAuthInfo struct {
Scopes []string Scopes []string
AuthUrl, TokenUrl string AuthUrl, TokenUrl string
Enabled bool Enabled bool
EmailAttributeName string
AllowedDomains []string AllowedDomains []string
HostedDomain string HostedDomain string
ApiUrl string ApiUrl string
......
...@@ -20,6 +20,7 @@ type SocialGenericOAuth struct { ...@@ -20,6 +20,7 @@ type SocialGenericOAuth struct {
allowedOrganizations []string allowedOrganizations []string
apiUrl string apiUrl string
allowSignup bool allowSignup bool
emailAttributeName string
teamIds []int teamIds []int
} }
...@@ -264,8 +265,9 @@ func (s *SocialGenericOAuth) extractEmail(data *UserInfoJson) string { ...@@ -264,8 +265,9 @@ func (s *SocialGenericOAuth) extractEmail(data *UserInfoJson) string {
return data.Email return data.Email
} }
if data.Attributes["email:primary"] != nil { emails, ok := data.Attributes[s.emailAttributeName]
return data.Attributes["email:primary"][0] if ok && len(emails) != 0 {
return emails[0]
} }
if data.Upn != "" { if data.Upn != "" {
......
...@@ -60,21 +60,22 @@ func NewOAuthService() { ...@@ -60,21 +60,22 @@ func NewOAuthService() {
for _, name := range allOauthes { for _, name := range allOauthes {
sec := setting.Raw.Section("auth." + name) sec := setting.Raw.Section("auth." + name)
info := &setting.OAuthInfo{ info := &setting.OAuthInfo{
ClientId: sec.Key("client_id").String(), ClientId: sec.Key("client_id").String(),
ClientSecret: sec.Key("client_secret").String(), ClientSecret: sec.Key("client_secret").String(),
Scopes: util.SplitString(sec.Key("scopes").String()), Scopes: util.SplitString(sec.Key("scopes").String()),
AuthUrl: sec.Key("auth_url").String(), AuthUrl: sec.Key("auth_url").String(),
TokenUrl: sec.Key("token_url").String(), TokenUrl: sec.Key("token_url").String(),
ApiUrl: sec.Key("api_url").String(), ApiUrl: sec.Key("api_url").String(),
Enabled: sec.Key("enabled").MustBool(), Enabled: sec.Key("enabled").MustBool(),
AllowedDomains: util.SplitString(sec.Key("allowed_domains").String()), EmailAttributeName: sec.Key("email_attribute_name").String(),
HostedDomain: sec.Key("hosted_domain").String(), AllowedDomains: util.SplitString(sec.Key("allowed_domains").String()),
AllowSignup: sec.Key("allow_sign_up").MustBool(), HostedDomain: sec.Key("hosted_domain").String(),
Name: sec.Key("name").MustString(name), AllowSignup: sec.Key("allow_sign_up").MustBool(),
TlsClientCert: sec.Key("tls_client_cert").String(), Name: sec.Key("name").MustString(name),
TlsClientKey: sec.Key("tls_client_key").String(), TlsClientCert: sec.Key("tls_client_cert").String(),
TlsClientCa: sec.Key("tls_client_ca").String(), TlsClientKey: sec.Key("tls_client_key").String(),
TlsSkipVerify: sec.Key("tls_skip_verify_insecure").MustBool(), TlsClientCa: sec.Key("tls_client_ca").String(),
TlsSkipVerify: sec.Key("tls_skip_verify_insecure").MustBool(),
} }
if !info.Enabled { if !info.Enabled {
...@@ -153,6 +154,7 @@ func NewOAuthService() { ...@@ -153,6 +154,7 @@ func NewOAuthService() {
allowedDomains: info.AllowedDomains, allowedDomains: info.AllowedDomains,
apiUrl: info.ApiUrl, apiUrl: info.ApiUrl,
allowSignup: info.AllowSignup, allowSignup: info.AllowSignup,
emailAttributeName: info.EmailAttributeName,
teamIds: sec.Key("team_ids").Ints(","), teamIds: sec.Key("team_ids").Ints(","),
allowedOrganizations: util.SplitString(sec.Key("allowed_organizations").String()), allowedOrganizations: util.SplitString(sec.Key("allowed_organizations").String()),
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment