OAuth: Support client_secret_jwt for oauth providers when doing token exchange (#95455)
* added backend support for client_secret_jwt * added backend support for client_secret_jwt * added all logic to the exchange function (overloaded social exchange in azuread_oauth to handle managed identity client id) * ran yarn install to update lock file * added support for client_secret_jwt when managed_identity_client_id is null * added audience flag and changed exchange to directly access oauth config using .info * added logic in setting oauth.Config for supported client authentication values * added client_authentication, managed_identity_client_id, and audience to sample.ini file * using provided ctx in ManagedIdentityCallback function * added frontend support for federated identity credential auth * added client authentication field * added Azure AD documentation for Grafana * added bold font to "Add" keyword in documentation * minor wording change relating to previous commit * addressed changing audience to federated_credential_audience, moving validation, and changing managedIdentityCallback to private function * correction to audience name changing * fixed orgMappingClientAuthentication function name, and added in logic into validateFederatedCredentialAudience function * Change docs * Add iam team as owner of azcore pkg * added backend support for client_secret_jwt * added all logic to the exchange function (overloaded social exchange in azuread_oauth to handle managed identity client id) * ran yarn install to update lock file * added support for client_secret_jwt when managed_identity_client_id is null * added audience flag and changed exchange to directly access oauth config using .info * added logic in setting oauth.Config for supported client authentication values * added client_authentication, managed_identity_client_id, and audience to sample.ini file * using provided ctx in ManagedIdentityCallback function * added frontend support for federated identity credential auth * added client authentication field * added Azure AD documentation for Grafana * added bold font to "Add" keyword in documentation * minor wording change relating to previous commit * addressed changing audience to federated_credential_audience, moving validation, and changing managedIdentityCallback to private function * correction to audience name changing * fixed orgMappingClientAuthentication function name, and added in logic into validateFederatedCredentialAudience function * Change docs * Add iam team as owner of azcore pkg * updated yarn lock file * updated doc for correction * removed wrong changes in pkg directory * removed newline in dashboard-generate.yaml and unified.ts * updated yarn.lock to match upstream * Lint Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * removing unwanted changes * added back removed newline * fixed failing test in azuread_oauth_test.go * Update azuread_oauth.go removed unnecessary newline, fixed lint --------- Signed-off-by: Jack Baldry <jack.baldry@grafana.com> Co-authored-by: Mihaly Gyongyosi <mgyongyosi@users.noreply.github.com> Co-authored-by: Jack Baldry <jack.baldry@grafana.com>
This commit is contained in:
co-authored by
Mihaly Gyongyosi
Jack Baldry
parent
d96f378562
commit
79d565f285
@@ -10,6 +10,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
|
||||
jose "github.com/go-jose/go-jose/v3"
|
||||
"github.com/go-jose/go-jose/v3/jwt"
|
||||
"github.com/google/uuid"
|
||||
@@ -37,6 +39,14 @@ var (
|
||||
errAzureADMissingGroups = &SocialError{"either the user does not have any group membership or the groups claim is missing from the token."}
|
||||
)
|
||||
|
||||
// List of supported audiences in Azure
|
||||
var supportedFederatedCredentialAudiences = []string{
|
||||
"api://AzureADTokenExchange", // Public
|
||||
"api://AzureADTokenExchangeUSGov", // US Gov
|
||||
"api://AzureADTokenExchangeChina", // Mooncake
|
||||
"api://AzureADTokenExchangeUSNat", // USNat
|
||||
"api://AzureADTokenExchangeUSSec"} // USSec
|
||||
|
||||
var _ social.SocialConnector = (*SocialAzureAD)(nil)
|
||||
var _ ssosettings.Reloadable = (*SocialAzureAD)(nil)
|
||||
|
||||
@@ -168,6 +178,64 @@ func (s *SocialAzureAD) UserInfo(ctx context.Context, client *http.Client, token
|
||||
return userInfo, nil
|
||||
}
|
||||
|
||||
func (s *SocialAzureAD) Exchange(ctx context.Context, code string, authOptions ...oauth2.AuthCodeOption) (*oauth2.Token, error) {
|
||||
s.reloadMutex.RLock()
|
||||
defer s.reloadMutex.RUnlock()
|
||||
|
||||
switch s.info.ClientAuthentication {
|
||||
case social.ManagedIdentity:
|
||||
// Generate client assertion
|
||||
clientAssertion, err := s.managedIdentityCallback(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set client assertion parameters
|
||||
authOptions = append(authOptions,
|
||||
oauth2.SetAuthURLParam("client_assertion", clientAssertion),
|
||||
oauth2.SetAuthURLParam("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"),
|
||||
)
|
||||
|
||||
case social.ClientSecretPost:
|
||||
// Default behavior for ClientSecretPost, no additional setup needed
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid client authentication method: %s", s.info.ClientAuthentication)
|
||||
}
|
||||
|
||||
// Default token exchange
|
||||
return s.Config.Exchange(ctx, code, authOptions...)
|
||||
}
|
||||
|
||||
// ManagedIdentityCallback retrieves a token using the managed identity credential of the Azure service.
|
||||
func (s *SocialAzureAD) managedIdentityCallback(ctx context.Context) (string, error) {
|
||||
// Validate required fields for Managed Identity authentication
|
||||
if s.info.ManagedIdentityClientID == "" {
|
||||
return "", fmt.Errorf("ManagedIdentityClientID is required for Managed Identity authentication")
|
||||
}
|
||||
if s.info.FederatedCredentialAudience == "" {
|
||||
return "", fmt.Errorf("FederatedCredentialAudience is required for Managed Identity authentication")
|
||||
}
|
||||
|
||||
// Prepare Managed Identity Credential
|
||||
mic, err := azidentity.NewManagedIdentityCredential(&azidentity.ManagedIdentityCredentialOptions{
|
||||
ID: azidentity.ClientID(s.info.ManagedIdentityClientID),
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error constructing managed identity credential: %w", err)
|
||||
}
|
||||
|
||||
// Request token and return
|
||||
tk, err := mic.GetToken(ctx, policy.TokenRequestOptions{
|
||||
Scopes: []string{fmt.Sprintf("%s/.default", s.info.FederatedCredentialAudience)},
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error getting managed identity token: %w", err)
|
||||
}
|
||||
|
||||
return tk.Token, nil
|
||||
}
|
||||
|
||||
func (s *SocialAzureAD) Reload(ctx context.Context, settings ssoModels.SSOSettings) error {
|
||||
newInfo, err := CreateOAuthInfoFromKeyValues(settings.Settings)
|
||||
if err != nil {
|
||||
@@ -206,6 +274,8 @@ func (s *SocialAzureAD) Validate(ctx context.Context, newSettings ssoModels.SSOS
|
||||
}
|
||||
|
||||
return validation.Validate(info, requester,
|
||||
validateClientAuthentication,
|
||||
validateFederatedCredentialAudience,
|
||||
validateAllowedGroups,
|
||||
validation.MustBeEmptyValidator(info.ApiUrl, "API URL"),
|
||||
validation.RequiredUrlValidator(info.AuthUrl, "Auth URL"),
|
||||
@@ -281,6 +351,40 @@ func (s *SocialAzureAD) validateIDTokenSignature(ctx context.Context, client *ht
|
||||
return nil, &SocialError{"AzureAD OAuth: signing key not found"}
|
||||
}
|
||||
|
||||
func validateFederatedCredentialAudience(info *social.OAuthInfo, requester identity.Requester) error {
|
||||
if info.ClientAuthentication != social.ManagedIdentity {
|
||||
return nil
|
||||
}
|
||||
for _, supportedFederatedCredentialAudience := range supportedFederatedCredentialAudiences {
|
||||
if info.FederatedCredentialAudience == supportedFederatedCredentialAudience {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return ssosettings.ErrInvalidOAuthConfig("FIC audience is not a supported audience.")
|
||||
}
|
||||
|
||||
func validateClientAuthentication(info *social.OAuthInfo, requester identity.Requester) error {
|
||||
switch info.ClientAuthentication {
|
||||
case social.ManagedIdentity:
|
||||
if info.ManagedIdentityClientID == "" {
|
||||
return ssosettings.ErrInvalidOAuthConfig("FIC managed identity client Id is required for Managed identity authentication.")
|
||||
}
|
||||
if info.FederatedCredentialAudience == "" {
|
||||
return ssosettings.ErrInvalidOAuthConfig("FIC audience is required for Managed identity authentication.")
|
||||
}
|
||||
return nil
|
||||
|
||||
case social.ClientSecretPost:
|
||||
if info.ClientSecret == "" {
|
||||
return ssosettings.ErrInvalidOAuthConfig("Client secret is required for Client secret authentication.")
|
||||
}
|
||||
return nil
|
||||
|
||||
default:
|
||||
return ssosettings.ErrInvalidOAuthConfig("Invalid client authentication method.")
|
||||
}
|
||||
}
|
||||
|
||||
func (claims *azureClaims) extractEmail() string {
|
||||
if claims.Email == "" {
|
||||
if claims.PreferredUsername != "" {
|
||||
|
||||
@@ -1138,7 +1138,9 @@ func TestSocialAzureAD_Validate(t *testing.T) {
|
||||
name: "SSOSettings is valid",
|
||||
settings: ssoModels.SSOSettings{
|
||||
Settings: map[string]any{
|
||||
"client_authentication": "client_secret_post",
|
||||
"client_id": "client-id",
|
||||
"client_secret": "client_secret",
|
||||
"allowed_groups": "0bb9c9cc-4945-418f-9b6a-c1d3b81141b0, 6034d328-0e6a-4240-8d03-cb9f2c1f16e4",
|
||||
"allow_assign_grafana_admin": "true",
|
||||
"auth_url": "https://example.com/auth",
|
||||
@@ -1147,6 +1149,22 @@ func TestSocialAzureAD_Validate(t *testing.T) {
|
||||
},
|
||||
requester: &user.SignedInUser{IsGrafanaAdmin: true},
|
||||
},
|
||||
{
|
||||
name: "SSOSettings is valid",
|
||||
settings: ssoModels.SSOSettings{
|
||||
Settings: map[string]any{
|
||||
"client_authentication": "managed_identity",
|
||||
"client_id": "client-id",
|
||||
"managed_identity_client_id": "managed-identity-client-id",
|
||||
"federated_credential_audience": "api://AzureADTokenExchange",
|
||||
"allowed_groups": "0bb9c9cc-4945-418f-9b6a-c1d3b81141b0, 6034d328-0e6a-4240-8d03-cb9f2c1f16e4",
|
||||
"allow_assign_grafana_admin": "true",
|
||||
"auth_url": "https://example.com/auth",
|
||||
"token_url": "https://example.com/token",
|
||||
},
|
||||
},
|
||||
requester: &user.SignedInUser{IsGrafanaAdmin: true},
|
||||
},
|
||||
{
|
||||
name: "fails if settings map contains an invalid field",
|
||||
settings: ssoModels.SSOSettings{
|
||||
|
||||
@@ -118,9 +118,19 @@ func createOAuthConfig(info *social.OAuthInfo, cfg *setting.Cfg, defaultName str
|
||||
authStyle = oauth2.AuthStyleAutoDetect
|
||||
}
|
||||
|
||||
var clientSecret string
|
||||
switch info.ClientAuthentication {
|
||||
case "client_secret_post":
|
||||
clientSecret = info.ClientSecret
|
||||
case "managed_identity":
|
||||
clientSecret = ""
|
||||
default:
|
||||
clientSecret = info.ClientSecret
|
||||
}
|
||||
|
||||
config := oauth2.Config{
|
||||
ClientID: info.ClientId,
|
||||
ClientSecret: info.ClientSecret,
|
||||
ClientSecret: clientSecret,
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: info.AuthUrl,
|
||||
TokenURL: info.TokenUrl,
|
||||
|
||||
@@ -119,8 +119,11 @@ func (s *SocialBase) getBaseSupportBundleContent(bf *bytes.Buffer) error {
|
||||
bf.WriteString(fmt.Sprintf("role_attribute_path = %v\n", s.info.RoleAttributePath))
|
||||
bf.WriteString(fmt.Sprintf("role_attribute_strict = %v\n", s.info.RoleAttributeStrict))
|
||||
bf.WriteString(fmt.Sprintf("skip_org_role_sync = %v\n", s.info.SkipOrgRoleSync))
|
||||
bf.WriteString(fmt.Sprintf("client_authentication = %v\n", s.info.ClientAuthentication))
|
||||
bf.WriteString(fmt.Sprintf("client_id = %v\n", s.Config.ClientID))
|
||||
bf.WriteString(fmt.Sprintf("client_secret = %v ; issue if empty\n", strings.Repeat("*", len(s.Config.ClientSecret))))
|
||||
bf.WriteString(fmt.Sprintf("managed_identity_client_id = %v\n", s.info.ManagedIdentityClientID))
|
||||
bf.WriteString(fmt.Sprintf("federated_credential_audience = %v\n", s.info.FederatedCredentialAudience))
|
||||
bf.WriteString(fmt.Sprintf("auth_url = %v\n", s.Config.Endpoint.AuthURL))
|
||||
bf.WriteString(fmt.Sprintf("token_url = %v\n", s.Config.Endpoint.TokenURL))
|
||||
bf.WriteString(fmt.Sprintf("auth_style = %v\n", s.Config.Endpoint.AuthStyle))
|
||||
|
||||
+44
-35
@@ -14,6 +14,12 @@ const (
|
||||
OfflineAccessScope = "offline_access"
|
||||
RoleGrafanaAdmin = "GrafanaAdmin" // For AzureAD for example this value cannot contain spaces
|
||||
|
||||
// Values for ClientAuthentication under OAuthInfo (based on oidc spec)
|
||||
ClientSecretPost = "client_secret_post"
|
||||
// Azure AD
|
||||
ManagedIdentity = "managed_identity"
|
||||
// Other providers...
|
||||
|
||||
AzureADProviderName = "azuread"
|
||||
GenericOAuthProviderName = "generic_oauth"
|
||||
GitHubProviderName = "github"
|
||||
@@ -53,41 +59,44 @@ type SocialConnector interface {
|
||||
}
|
||||
|
||||
type OAuthInfo struct {
|
||||
AllowAssignGrafanaAdmin bool `mapstructure:"allow_assign_grafana_admin" toml:"allow_assign_grafana_admin"`
|
||||
AllowSignup bool `mapstructure:"allow_sign_up" toml:"allow_sign_up"`
|
||||
AllowedDomains []string `mapstructure:"allowed_domains" toml:"allowed_domains"`
|
||||
AllowedGroups []string `mapstructure:"allowed_groups" toml:"allowed_groups"`
|
||||
ApiUrl string `mapstructure:"api_url" toml:"api_url"`
|
||||
AuthStyle string `mapstructure:"auth_style" toml:"auth_style"`
|
||||
AuthUrl string `mapstructure:"auth_url" toml:"auth_url"`
|
||||
AutoLogin bool `mapstructure:"auto_login" toml:"auto_login"`
|
||||
ClientId string `mapstructure:"client_id" toml:"client_id"`
|
||||
ClientSecret string `mapstructure:"client_secret" toml:"-"`
|
||||
EmailAttributeName string `mapstructure:"email_attribute_name" toml:"email_attribute_name"`
|
||||
EmailAttributePath string `mapstructure:"email_attribute_path" toml:"email_attribute_path"`
|
||||
EmptyScopes bool `mapstructure:"empty_scopes" toml:"empty_scopes"`
|
||||
Enabled bool `mapstructure:"enabled" toml:"enabled"`
|
||||
GroupsAttributePath string `mapstructure:"groups_attribute_path" toml:"groups_attribute_path"`
|
||||
HostedDomain string `mapstructure:"hosted_domain" toml:"hosted_domain"`
|
||||
Icon string `mapstructure:"icon" toml:"icon"`
|
||||
Name string `mapstructure:"name" toml:"name"`
|
||||
RoleAttributePath string `mapstructure:"role_attribute_path" toml:"role_attribute_path"`
|
||||
RoleAttributeStrict bool `mapstructure:"role_attribute_strict" toml:"role_attribute_strict"`
|
||||
OrgAttributePath string `mapstructure:"org_attribute_path"`
|
||||
OrgMapping []string `mapstructure:"org_mapping"`
|
||||
Scopes []string `mapstructure:"scopes" toml:"scopes"`
|
||||
SignoutRedirectUrl string `mapstructure:"signout_redirect_url" toml:"signout_redirect_url"`
|
||||
SkipOrgRoleSync bool `mapstructure:"skip_org_role_sync" toml:"skip_org_role_sync"`
|
||||
TeamIdsAttributePath string `mapstructure:"team_ids_attribute_path" toml:"team_ids_attribute_path"`
|
||||
TeamsUrl string `mapstructure:"teams_url" toml:"teams_url"`
|
||||
TlsClientCa string `mapstructure:"tls_client_ca" toml:"tls_client_ca"`
|
||||
TlsClientCert string `mapstructure:"tls_client_cert" toml:"tls_client_cert"`
|
||||
TlsClientKey string `mapstructure:"tls_client_key" toml:"tls_client_key"`
|
||||
TlsSkipVerify bool `mapstructure:"tls_skip_verify_insecure" toml:"tls_skip_verify_insecure"`
|
||||
TokenUrl string `mapstructure:"token_url" toml:"token_url"`
|
||||
UsePKCE bool `mapstructure:"use_pkce" toml:"use_pkce"`
|
||||
UseRefreshToken bool `mapstructure:"use_refresh_token" toml:"use_refresh_token"`
|
||||
Extra map[string]string `mapstructure:",remain" toml:"extra,omitempty"`
|
||||
AllowAssignGrafanaAdmin bool `mapstructure:"allow_assign_grafana_admin" toml:"allow_assign_grafana_admin"`
|
||||
AllowSignup bool `mapstructure:"allow_sign_up" toml:"allow_sign_up"`
|
||||
AllowedDomains []string `mapstructure:"allowed_domains" toml:"allowed_domains"`
|
||||
AllowedGroups []string `mapstructure:"allowed_groups" toml:"allowed_groups"`
|
||||
ApiUrl string `mapstructure:"api_url" toml:"api_url"`
|
||||
AuthStyle string `mapstructure:"auth_style" toml:"auth_style"`
|
||||
AuthUrl string `mapstructure:"auth_url" toml:"auth_url"`
|
||||
AutoLogin bool `mapstructure:"auto_login" toml:"auto_login"`
|
||||
ClientAuthentication string `mapstructure:"client_authentication" toml:"client_authentication"`
|
||||
ClientId string `mapstructure:"client_id" toml:"client_id"`
|
||||
ClientSecret string `mapstructure:"client_secret" toml:"-"`
|
||||
ManagedIdentityClientID string `mapstructure:"managed_identity_client_id" toml:"managed_identity_client_id"`
|
||||
FederatedCredentialAudience string `mapstructure:"federated_credential_audience" toml:"federated_credential_audience"`
|
||||
EmailAttributeName string `mapstructure:"email_attribute_name" toml:"email_attribute_name"`
|
||||
EmailAttributePath string `mapstructure:"email_attribute_path" toml:"email_attribute_path"`
|
||||
EmptyScopes bool `mapstructure:"empty_scopes" toml:"empty_scopes"`
|
||||
Enabled bool `mapstructure:"enabled" toml:"enabled"`
|
||||
GroupsAttributePath string `mapstructure:"groups_attribute_path" toml:"groups_attribute_path"`
|
||||
HostedDomain string `mapstructure:"hosted_domain" toml:"hosted_domain"`
|
||||
Icon string `mapstructure:"icon" toml:"icon"`
|
||||
Name string `mapstructure:"name" toml:"name"`
|
||||
RoleAttributePath string `mapstructure:"role_attribute_path" toml:"role_attribute_path"`
|
||||
RoleAttributeStrict bool `mapstructure:"role_attribute_strict" toml:"role_attribute_strict"`
|
||||
OrgAttributePath string `mapstructure:"org_attribute_path"`
|
||||
OrgMapping []string `mapstructure:"org_mapping"`
|
||||
Scopes []string `mapstructure:"scopes" toml:"scopes"`
|
||||
SignoutRedirectUrl string `mapstructure:"signout_redirect_url" toml:"signout_redirect_url"`
|
||||
SkipOrgRoleSync bool `mapstructure:"skip_org_role_sync" toml:"skip_org_role_sync"`
|
||||
TeamIdsAttributePath string `mapstructure:"team_ids_attribute_path" toml:"team_ids_attribute_path"`
|
||||
TeamsUrl string `mapstructure:"teams_url" toml:"teams_url"`
|
||||
TlsClientCa string `mapstructure:"tls_client_ca" toml:"tls_client_ca"`
|
||||
TlsClientCert string `mapstructure:"tls_client_cert" toml:"tls_client_cert"`
|
||||
TlsClientKey string `mapstructure:"tls_client_key" toml:"tls_client_key"`
|
||||
TlsSkipVerify bool `mapstructure:"tls_skip_verify_insecure" toml:"tls_skip_verify_insecure"`
|
||||
TokenUrl string `mapstructure:"token_url" toml:"token_url"`
|
||||
UsePKCE bool `mapstructure:"use_pkce" toml:"use_pkce"`
|
||||
UseRefreshToken bool `mapstructure:"use_refresh_token" toml:"use_refresh_token"`
|
||||
Extra map[string]string `mapstructure:",remain" toml:"extra,omitempty"`
|
||||
}
|
||||
|
||||
func NewOAuthInfo() *OAuthInfo {
|
||||
|
||||
@@ -218,8 +218,11 @@ icon = signin
|
||||
enabled = true
|
||||
allow_sign_up = false
|
||||
auto_login = true
|
||||
client_authentication = test_client_authentication
|
||||
client_id = test_client_id
|
||||
client_secret = test_client_secret
|
||||
managed_identity_client_id = test_managed_identity_client_id
|
||||
federated_credential_audience = test_federated_credential_audience
|
||||
scopes = ["openid", "profile", "email"]
|
||||
empty_scopes = false
|
||||
email_attribute_name = email:primary
|
||||
@@ -257,38 +260,41 @@ signout_redirect_url = https://oauth.com/signout?post_logout_redirect_uri=https:
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedOAuthInfo := &social.OAuthInfo{
|
||||
Name: "OAuth",
|
||||
Icon: "signin",
|
||||
Enabled: true,
|
||||
AllowSignup: false,
|
||||
AutoLogin: true,
|
||||
ClientId: "test_client_id",
|
||||
ClientSecret: "test_client_secret",
|
||||
Scopes: []string{"openid", "profile", "email"},
|
||||
EmptyScopes: false,
|
||||
EmailAttributeName: "email:primary",
|
||||
EmailAttributePath: "email",
|
||||
RoleAttributePath: "role",
|
||||
RoleAttributeStrict: true,
|
||||
GroupsAttributePath: "groups",
|
||||
TeamIdsAttributePath: "team_ids",
|
||||
AuthUrl: "test_auth_url",
|
||||
TokenUrl: "test_token_url",
|
||||
ApiUrl: "test_api_url",
|
||||
TeamsUrl: "test_teams_url",
|
||||
AllowedDomains: []string{"domain1.com"},
|
||||
AllowedGroups: []string{},
|
||||
TlsSkipVerify: true,
|
||||
TlsClientCert: "",
|
||||
TlsClientKey: "",
|
||||
TlsClientCa: "",
|
||||
UsePKCE: false,
|
||||
AuthStyle: "",
|
||||
AllowAssignGrafanaAdmin: true,
|
||||
UseRefreshToken: true,
|
||||
SkipOrgRoleSync: true,
|
||||
HostedDomain: "test_hosted_domain",
|
||||
SignoutRedirectUrl: "https://oauth.com/signout?post_logout_redirect_uri=https://grafana.com",
|
||||
Name: "OAuth",
|
||||
Icon: "signin",
|
||||
Enabled: true,
|
||||
AllowSignup: false,
|
||||
AutoLogin: true,
|
||||
ClientAuthentication: "test_client_authentication",
|
||||
ClientId: "test_client_id",
|
||||
ClientSecret: "test_client_secret",
|
||||
ManagedIdentityClientID: "test_managed_identity_client_id",
|
||||
FederatedCredentialAudience: "test_federated_credential_audience",
|
||||
Scopes: []string{"openid", "profile", "email"},
|
||||
EmptyScopes: false,
|
||||
EmailAttributeName: "email:primary",
|
||||
EmailAttributePath: "email",
|
||||
RoleAttributePath: "role",
|
||||
RoleAttributeStrict: true,
|
||||
GroupsAttributePath: "groups",
|
||||
TeamIdsAttributePath: "team_ids",
|
||||
AuthUrl: "test_auth_url",
|
||||
TokenUrl: "test_token_url",
|
||||
ApiUrl: "test_api_url",
|
||||
TeamsUrl: "test_teams_url",
|
||||
AllowedDomains: []string{"domain1.com"},
|
||||
AllowedGroups: []string{},
|
||||
TlsSkipVerify: true,
|
||||
TlsClientCert: "",
|
||||
TlsClientKey: "",
|
||||
TlsClientCa: "",
|
||||
UsePKCE: false,
|
||||
AuthStyle: "",
|
||||
AllowAssignGrafanaAdmin: true,
|
||||
UseRefreshToken: true,
|
||||
SkipOrgRoleSync: true,
|
||||
HostedDomain: "test_hosted_domain",
|
||||
SignoutRedirectUrl: "https://oauth.com/signout?post_logout_redirect_uri=https://grafana.com",
|
||||
Extra: map[string]string{
|
||||
"allowed_organizations": "org1, org2",
|
||||
"id_token_attribute_name": "id_token",
|
||||
|
||||
@@ -70,40 +70,43 @@ func (s *OAuthStrategy) loadSettingsForProvider(provider string) map[string]any
|
||||
section := s.cfg.Raw.Section("auth." + provider)
|
||||
|
||||
result := map[string]any{
|
||||
"client_id": section.Key("client_id").Value(),
|
||||
"client_secret": section.Key("client_secret").Value(),
|
||||
"scopes": section.Key("scopes").Value(),
|
||||
"empty_scopes": section.Key("empty_scopes").MustBool(false),
|
||||
"auth_style": section.Key("auth_style").Value(),
|
||||
"auth_url": section.Key("auth_url").Value(),
|
||||
"token_url": section.Key("token_url").Value(),
|
||||
"api_url": section.Key("api_url").Value(),
|
||||
"teams_url": section.Key("teams_url").Value(),
|
||||
"enabled": section.Key("enabled").MustBool(false),
|
||||
"email_attribute_name": section.Key("email_attribute_name").Value(),
|
||||
"email_attribute_path": section.Key("email_attribute_path").Value(),
|
||||
"role_attribute_path": section.Key("role_attribute_path").Value(),
|
||||
"role_attribute_strict": section.Key("role_attribute_strict").MustBool(false),
|
||||
"groups_attribute_path": section.Key("groups_attribute_path").Value(),
|
||||
"team_ids_attribute_path": section.Key("team_ids_attribute_path").Value(),
|
||||
"allowed_domains": section.Key("allowed_domains").Value(),
|
||||
"hosted_domain": section.Key("hosted_domain").Value(),
|
||||
"allow_sign_up": section.Key("allow_sign_up").MustBool(false),
|
||||
"name": section.Key("name").Value(),
|
||||
"icon": section.Key("icon").Value(),
|
||||
"skip_org_role_sync": section.Key("skip_org_role_sync").MustBool(false),
|
||||
"tls_client_cert": section.Key("tls_client_cert").Value(),
|
||||
"tls_client_key": section.Key("tls_client_key").Value(),
|
||||
"tls_client_ca": section.Key("tls_client_ca").Value(),
|
||||
"tls_skip_verify_insecure": section.Key("tls_skip_verify_insecure").MustBool(false),
|
||||
"use_pkce": section.Key("use_pkce").MustBool(false),
|
||||
"use_refresh_token": section.Key("use_refresh_token").MustBool(false),
|
||||
"allow_assign_grafana_admin": section.Key("allow_assign_grafana_admin").MustBool(false),
|
||||
"auto_login": section.Key("auto_login").MustBool(false),
|
||||
"allowed_groups": section.Key("allowed_groups").Value(),
|
||||
"signout_redirect_url": section.Key("signout_redirect_url").Value(),
|
||||
"org_mapping": section.Key("org_mapping").Value(),
|
||||
"org_attribute_path": section.Key("org_attribute_path").Value(),
|
||||
"client_authentication": section.Key("client_authentication").Value(),
|
||||
"client_id": section.Key("client_id").Value(),
|
||||
"client_secret": section.Key("client_secret").Value(),
|
||||
"managed_identity_client_id": section.Key("managed_identity_client_id").Value(),
|
||||
"federated_credential_audience": section.Key("federated_credential_audience").Value(),
|
||||
"scopes": section.Key("scopes").Value(),
|
||||
"empty_scopes": section.Key("empty_scopes").MustBool(false),
|
||||
"auth_style": section.Key("auth_style").Value(),
|
||||
"auth_url": section.Key("auth_url").Value(),
|
||||
"token_url": section.Key("token_url").Value(),
|
||||
"api_url": section.Key("api_url").Value(),
|
||||
"teams_url": section.Key("teams_url").Value(),
|
||||
"enabled": section.Key("enabled").MustBool(false),
|
||||
"email_attribute_name": section.Key("email_attribute_name").Value(),
|
||||
"email_attribute_path": section.Key("email_attribute_path").Value(),
|
||||
"role_attribute_path": section.Key("role_attribute_path").Value(),
|
||||
"role_attribute_strict": section.Key("role_attribute_strict").MustBool(false),
|
||||
"groups_attribute_path": section.Key("groups_attribute_path").Value(),
|
||||
"team_ids_attribute_path": section.Key("team_ids_attribute_path").Value(),
|
||||
"allowed_domains": section.Key("allowed_domains").Value(),
|
||||
"hosted_domain": section.Key("hosted_domain").Value(),
|
||||
"allow_sign_up": section.Key("allow_sign_up").MustBool(false),
|
||||
"name": section.Key("name").Value(),
|
||||
"icon": section.Key("icon").Value(),
|
||||
"skip_org_role_sync": section.Key("skip_org_role_sync").MustBool(false),
|
||||
"tls_client_cert": section.Key("tls_client_cert").Value(),
|
||||
"tls_client_key": section.Key("tls_client_key").Value(),
|
||||
"tls_client_ca": section.Key("tls_client_ca").Value(),
|
||||
"tls_skip_verify_insecure": section.Key("tls_skip_verify_insecure").MustBool(false),
|
||||
"use_pkce": section.Key("use_pkce").MustBool(false),
|
||||
"use_refresh_token": section.Key("use_refresh_token").MustBool(false),
|
||||
"allow_assign_grafana_admin": section.Key("allow_assign_grafana_admin").MustBool(false),
|
||||
"auto_login": section.Key("auto_login").MustBool(false),
|
||||
"allowed_groups": section.Key("allowed_groups").Value(),
|
||||
"signout_redirect_url": section.Key("signout_redirect_url").Value(),
|
||||
"org_mapping": section.Key("org_mapping").Value(),
|
||||
"org_attribute_path": section.Key("org_attribute_path").Value(),
|
||||
}
|
||||
|
||||
extraKeys := extraKeysByProvider[provider]
|
||||
|
||||
@@ -19,8 +19,11 @@ var (
|
||||
enabled = true
|
||||
allow_sign_up = false
|
||||
auto_login = true
|
||||
client_authentication = test_client_authentication
|
||||
client_id = test_client_id
|
||||
client_secret = test_client_secret
|
||||
managed_identity_client_id = test_managed_identity_client_id
|
||||
federated_credential_audience = test_federated_credential_audience
|
||||
scopes = openid, profile, email
|
||||
empty_scopes = false
|
||||
email_attribute_name = email:primary
|
||||
@@ -57,45 +60,48 @@ var (
|
||||
`
|
||||
|
||||
expectedOAuthInfo = map[string]any{
|
||||
"name": "OAuth",
|
||||
"icon": "signin",
|
||||
"enabled": true,
|
||||
"allow_sign_up": false,
|
||||
"auto_login": true,
|
||||
"client_id": "test_client_id",
|
||||
"client_secret": "test_client_secret",
|
||||
"scopes": "openid, profile, email",
|
||||
"empty_scopes": false,
|
||||
"email_attribute_name": "email:primary",
|
||||
"email_attribute_path": "email",
|
||||
"role_attribute_path": "role",
|
||||
"role_attribute_strict": true,
|
||||
"groups_attribute_path": "groups",
|
||||
"team_ids_attribute_path": "team_ids",
|
||||
"auth_url": "test_auth_url",
|
||||
"token_url": "test_token_url",
|
||||
"api_url": "test_api_url",
|
||||
"teams_url": "test_teams_url",
|
||||
"allowed_domains": "domain1.com",
|
||||
"allowed_groups": "",
|
||||
"tls_skip_verify_insecure": true,
|
||||
"tls_client_cert": "",
|
||||
"tls_client_key": "",
|
||||
"tls_client_ca": "",
|
||||
"use_pkce": false,
|
||||
"auth_style": "inheader",
|
||||
"allow_assign_grafana_admin": true,
|
||||
"use_refresh_token": true,
|
||||
"hosted_domain": "test_hosted_domain",
|
||||
"skip_org_role_sync": true,
|
||||
"signout_redirect_url": "test_signout_redirect_url",
|
||||
"allowed_organizations": "org1, org2",
|
||||
"id_token_attribute_name": "id_token",
|
||||
"login_attribute_path": "login",
|
||||
"name_attribute_path": "name",
|
||||
"team_ids": "first, second",
|
||||
"org_attribute_path": "groups",
|
||||
"org_mapping": "Group1:*:Editor",
|
||||
"name": "OAuth",
|
||||
"icon": "signin",
|
||||
"enabled": true,
|
||||
"allow_sign_up": false,
|
||||
"auto_login": true,
|
||||
"client_authentication": "test_client_authentication",
|
||||
"client_id": "test_client_id",
|
||||
"client_secret": "test_client_secret",
|
||||
"managed_identity_client_id": "test_managed_identity_client_id",
|
||||
"federated_credential_audience": "test_federated_credential_audience",
|
||||
"scopes": "openid, profile, email",
|
||||
"empty_scopes": false,
|
||||
"email_attribute_name": "email:primary",
|
||||
"email_attribute_path": "email",
|
||||
"role_attribute_path": "role",
|
||||
"role_attribute_strict": true,
|
||||
"groups_attribute_path": "groups",
|
||||
"team_ids_attribute_path": "team_ids",
|
||||
"auth_url": "test_auth_url",
|
||||
"token_url": "test_token_url",
|
||||
"api_url": "test_api_url",
|
||||
"teams_url": "test_teams_url",
|
||||
"allowed_domains": "domain1.com",
|
||||
"allowed_groups": "",
|
||||
"tls_skip_verify_insecure": true,
|
||||
"tls_client_cert": "",
|
||||
"tls_client_key": "",
|
||||
"tls_client_ca": "",
|
||||
"use_pkce": false,
|
||||
"auth_style": "inheader",
|
||||
"allow_assign_grafana_admin": true,
|
||||
"use_refresh_token": true,
|
||||
"hosted_domain": "test_hosted_domain",
|
||||
"skip_org_role_sync": true,
|
||||
"signout_redirect_url": "test_signout_redirect_url",
|
||||
"allowed_organizations": "org1, org2",
|
||||
"id_token_attribute_name": "id_token",
|
||||
"login_attribute_path": "login",
|
||||
"name_attribute_path": "name",
|
||||
"team_ids": "first, second",
|
||||
"org_attribute_path": "groups",
|
||||
"org_mapping": "Group1:*:Editor",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user