Merge pull request #14563 from tdabasinskas/broken_oauth_provider

Support OAuth providers that are not RFC6749 compliant
This commit is contained in:
Carl Bergquist 2018-12-19 16:05:53 +01:00 committed by GitHub
commit c201fc170f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 54 additions and 31 deletions

View File

@ -335,6 +335,7 @@ tls_skip_verify_insecure = false
tls_client_cert = tls_client_cert =
tls_client_key = tls_client_key =
tls_client_ca = tls_client_ca =
send_client_credentials_via_post = false
#################################### Basic Auth ########################## #################################### Basic Auth ##########################
[auth.basic] [auth.basic]

View File

@ -284,6 +284,10 @@ log_queries =
;tls_client_key = ;tls_client_key =
;tls_client_ca = ;tls_client_ca =
; Set to true to enable sending client_id and client_secret via POST body instead of Basic authentication HTTP header
; This might be required if the OAuth provider is not RFC6749 compliant, only supporting credentials passed via POST payload
;send_client_credentials_via_post = false
#################################### Grafana.com Auth #################### #################################### Grafana.com Auth ####################
[auth.grafana_com] [auth.grafana_com]
;enabled = false ;enabled = false

View File

@ -17,7 +17,7 @@ can find examples using Okta, BitBucket, OneLogin and Azure.
This callback URL must match the full HTTP address that you use in your browser to access Grafana, but with the prefix path of `/login/generic_oauth`. This callback URL must match the full HTTP address that you use in your browser to access Grafana, but with the prefix path of `/login/generic_oauth`.
You may have to set the `root_url` option of `[server]` for the callback URL to be You may have to set the `root_url` option of `[server]` for the callback URL to be
correct. For example in case you are serving Grafana behind a proxy. correct. For example in case you are serving Grafana behind a proxy.
Example config: Example config:
@ -209,6 +209,17 @@ allowed_organizations =
token_url = https://<your domain>.my.centrify.com/OAuth2/Token/<Application ID> token_url = https://<your domain>.my.centrify.com/OAuth2/Token/<Application ID>
``` ```
## Set up OAuth2 with non-compliant providers
Some OAuth2 providers might not support `client_id` and `client_secret` passed via Basic Authentication HTTP header, which
results in `invalid_client` error. To allow Grafana to authenticate via these type of providers, the client identifiers must be
send via POST body, which can be enabled via the following settings:
```bash
[auth.generic_oauth]
send_client_credentials_via_post = true
```
<hr> <hr>

View File

@ -1,20 +1,21 @@
package setting package setting
type OAuthInfo struct { type OAuthInfo struct {
ClientId, ClientSecret string ClientId, ClientSecret string
Scopes []string Scopes []string
AuthUrl, TokenUrl string AuthUrl, TokenUrl string
Enabled bool Enabled bool
EmailAttributeName string EmailAttributeName string
AllowedDomains []string AllowedDomains []string
HostedDomain string HostedDomain string
ApiUrl string ApiUrl string
AllowSignup bool AllowSignup bool
Name string Name string
TlsClientCert string TlsClientCert string
TlsClientKey string TlsClientKey string
TlsClientCa string TlsClientCa string
TlsSkipVerify bool TlsSkipVerify bool
SendClientCredentialsViaPost bool
} }
type OAuther struct { type OAuther struct {

View File

@ -63,28 +63,34 @@ 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(),
EmailAttributeName: sec.Key("email_attribute_name").String(), EmailAttributeName: sec.Key("email_attribute_name").String(),
AllowedDomains: util.SplitString(sec.Key("allowed_domains").String()), AllowedDomains: util.SplitString(sec.Key("allowed_domains").String()),
HostedDomain: sec.Key("hosted_domain").String(), HostedDomain: sec.Key("hosted_domain").String(),
AllowSignup: sec.Key("allow_sign_up").MustBool(), AllowSignup: sec.Key("allow_sign_up").MustBool(),
Name: sec.Key("name").MustString(name), Name: sec.Key("name").MustString(name),
TlsClientCert: sec.Key("tls_client_cert").String(), TlsClientCert: sec.Key("tls_client_cert").String(),
TlsClientKey: sec.Key("tls_client_key").String(), TlsClientKey: sec.Key("tls_client_key").String(),
TlsClientCa: sec.Key("tls_client_ca").String(), TlsClientCa: sec.Key("tls_client_ca").String(),
TlsSkipVerify: sec.Key("tls_skip_verify_insecure").MustBool(), TlsSkipVerify: sec.Key("tls_skip_verify_insecure").MustBool(),
SendClientCredentialsViaPost: sec.Key("send_client_credentials_via_post").MustBool(),
} }
if !info.Enabled { if !info.Enabled {
continue continue
} }
// handle the clients that do not properly support Basic auth headers and require passing client_id/client_secret via POST payload
if info.SendClientCredentialsViaPost {
oauth2.RegisterBrokenAuthHeaderProvider(info.TokenUrl)
}
if name == "grafananet" { if name == "grafananet" {
name = grafanaCom name = grafanaCom
} }