瀏覽代碼

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
Bob Shannon 7 年之前
父節點
當前提交
f257ff0216
共有 5 個文件被更改,包括 31 次插入18 次删除
  1. 1 0
      conf/defaults.ini
  2. 8 1
      docs/sources/auth/generic-oauth.md
  3. 1 0
      pkg/setting/setting_oauth.go
  4. 4 2
      pkg/social/generic_oauth.go
  5. 17 15
      pkg/social/social.go

+ 1 - 0
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 =

+ 8 - 1
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
 

+ 1 - 0
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

+ 4 - 2
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 != "" {

+ 17 - 15
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()),
 			}