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>
pull/98587/head
John Naizer 6 months ago committed by GitHub
parent d96f378562
commit 79d565f285
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 3
      conf/defaults.ini
  2. 3
      conf/sample.ini
  3. 51
      docs/sources/setup-grafana/configure-security/configure-authentication/azuread/index.md
  4. 2
      go.mod
  5. 104
      pkg/login/social/connectors/azuread_oauth.go
  6. 18
      pkg/login/social/connectors/azuread_oauth_test.go
  7. 12
      pkg/login/social/connectors/common.go
  8. 3
      pkg/login/social/connectors/social_base.go
  9. 9
      pkg/login/social/social.go
  10. 6
      pkg/login/social/socialimpl/service_test.go
  11. 3
      pkg/services/ssosettings/strategies/oauth_strategy.go
  12. 6
      pkg/services/ssosettings/strategies/oauth_strategy_test.go
  13. 39
      public/app/features/auth-config/fields.tsx
  14. 3
      public/app/features/auth-config/types.ts

@ -779,8 +779,11 @@ icon = microsoft
enabled = false
allow_sign_up = true
auto_login = false
client_authentication =
client_id = some_client_id
client_secret =
managed_identity_client_id =
federated_credential_audience =
scopes = openid email profile
auth_url = https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/authorize
token_url = https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token

@ -758,8 +758,11 @@
;enabled = false
;allow_sign_up = true
;auto_login = false
;client_authentication =
;client_id = some_client_id
;client_secret = some_client_secret
;managed_identity_client_id =
;federated_credential_audience =
;scopes = openid email profile
;auth_url = https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/authorize
;token_url = https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token

@ -46,16 +46,44 @@ To enable the Azure AD/Entra ID OAuth, register your application with Entra ID.
- Note the **OAuth 2.0 authorization endpoint (v2)** URL. This is the authorization URL.
- Note the **OAuth 2.0 token endpoint (v2)**. This is the token URL.
1. Click **Certificates & secrets** in the side menu, then add a new entry under **Client secrets** with the following configuration.
1. Click **Certificates & secrets** in the side menu, then add a new entry under the supported client authentication option you want to use. The following are the supported client authentication options with their respective configuration steps.
- Description: Grafana OAuth
- **Client secrets**
1. Add a new entry under **Client secrets** with the following configuration.
- Description: Grafana OAuth 2.0
- Expires: Select an expiration period
1. Click **Add** then copy the key **Value**. This is the OAuth client secret.
1. Click **Add** then copy the key **Value**. This is the OAuth 2.0 client secret.
{{% admonition type="note" %}}
Make sure that you copy the string in the **Value** field, rather than the one in the **Secret ID** field.
{{% /admonition %}}
{{< admonition type="note" >}}
Make sure that you copy the string in the **Value** field, rather than the one in the **Secret ID** field.
{{< /admonition >}}
1. You must have set `client_authentication` under `[auth.azuread]` to `client_secret_post` in the Grafana server configuration for this to work.
- **Federated credentials**
1. Refer to [Configure an application to trust a managed identity (preview)](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-config-app-trust-managed-identity?tabs=microsoft-entra-admin-center) for a complete guide on setting up a managed identity as a federated credential.
Add a new entry under Federated credentials with the following configuration.
- Federated credential scenario: Select **Other issuer**.
- Issuer: The OAuth 2.0 / OIDC issuer URL of the Microsoft Entra ID authority. For example: `https://login.microsoftonline.com/{tenantID}/v2.0`.
- Subject identifier: The Object (Principal) ID GUID of the Managed Identity.
- Name: A unique descriptive name for the credential.
- Description: Grafana OAuth.
- Audience: The audience value that must appear in the external token. For Public cloud, it would be `api://AzureADTokenExchange`. See mentioned documentation for the full list of available audiences.
1. Click **Add**, and then copy the Managed Identity Client ID and the federated credential Audience values. This is your OAuth 2.0 federated credential.
1. You must have set `client_authentication` under `[auth.azuread]` to `managed_identity` in the Grafana server configuration for this to work.
{{< admonition type="note" >}}
Managed identities as federated credentials are only applicable to workloads hosted in Azure.
You can only add user-assigned managed identities as federated credentials on Entra ID applications.
{{< /admonition >}}
1. Define the required application roles for Grafana [using the Azure Portal](#configure-application-roles-for-grafana-in-the-azure-portal) or [using the manifest file](#configure-application-roles-for-grafana-in-the-manifest-file).
@ -204,8 +232,11 @@ resource "grafana_sso_settings" "azuread_sso_settings" {
name = "Azure AD"
auth_url = "https://login.microsoftonline.com/TENANT_ID/oauth2/v2.0/authorize"
token_url = "https://login.microsoftonline.com/TENANT_ID/oauth2/v2.0/token"
client_authentication = "CLIENT_AUTHENTICATION_OPTION"
client_id = "APPLICATION_ID"
client_secret = "CLIENT_SECRET"
managed_identity_client_id = "MANAGED_IDENTITY_CLIENT_ID"
federated_credential_audience = "FEDERATED_CREDENTIAL_AUDIENCE"
allow_sign_up = true
auto_login = false
scopes = "openid email profile"
@ -234,8 +265,11 @@ name = Azure AD
enabled = true
allow_sign_up = true
auto_login = false
client_authentication = CLIENT_AUTHENTICATION_OPTION
client_id = APPLICATION_ID
client_secret = CLIENT_SECRET
managed_identity_client_id = MANAGED_IDENTITY_CLIENT_ID
federated_credential_audience = FEDERATED_CREDENTIAL_AUDIENCE
scopes = openid email profile
auth_url = https://login.microsoftonline.com/TENANT_ID/oauth2/v2.0/authorize
token_url = https://login.microsoftonline.com/TENANT_ID/oauth2/v2.0/token
@ -248,11 +282,14 @@ skip_org_role_sync = false
use_pkce = true
```
You can also use these environment variables to configure **client_id** and **client_secret**:
You can also use these environment variables to configure `client_authentication`, `client_id`, `client_secret`, `managed_identity_client_id`, and `federated_credential_audience`:
```
GF_AUTH_AZUREAD_CLIENT_AUTHENTICATION
GF_AUTH_AZUREAD_CLIENT_ID
GF_AUTH_AZUREAD_CLIENT_SECRET
GF_AUTH_AZUREAD_MANAGED_IDENTITY_CLIENT_ID
GF_AUTH_AZUREAD_FEDERATED_CREDENTIAL_AUDIENCE
```
{{% admonition type="note" %}}

@ -209,7 +209,7 @@ require (
cloud.google.com/go/compute/metadata v0.5.0 // indirect
cloud.google.com/go/iam v1.2.0 // indirect
github.com/Azure/azure-pipeline-go v0.2.3 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 // @grafana/identity-access-team
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect

@ -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,25 @@ 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",
"token_url": "https://example.com/token",
},
},
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",

@ -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))

@ -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"
@ -61,8 +67,11 @@ type OAuthInfo struct {
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"`

@ -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
@ -262,8 +265,11 @@ signout_redirect_url = https://oauth.com/signout?post_logout_redirect_uri=https:
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",

@ -70,8 +70,11 @@ func (s *OAuthStrategy) loadSettingsForProvider(provider string) map[string]any
section := s.cfg.Raw.Section("auth." + provider)
result := map[string]any{
"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(),

@ -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
@ -62,8 +65,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",

@ -1,5 +1,6 @@
import { validate as uuidValidate } from 'uuid';
import { SelectableValue } from '@grafana/data';
import { config } from '@grafana/runtime';
import { TextLink } from '@grafana/ui';
import { contextSrv } from 'app/core/core';
@ -26,8 +27,11 @@ export const sectionFields: Section = {
id: 'general',
fields: [
'name',
'clientAuthentication',
'clientId',
'clientSecret',
'managedIdentityClientId',
'federatedCredentialAudience',
'scopes',
'authUrl',
'tokenUrl',
@ -248,6 +252,18 @@ export const sectionFields: Section = {
*/
export function fieldMap(provider: string): Record<string, FieldData> {
return {
clientAuthentication: {
label: 'Client authentication',
type: 'select',
description: 'The client authentication method used to authenticate to the token endpoint.',
multi: false,
options: clientAuthenticationOptions(provider),
defaultValue: { value: 'client_secret_post', label: 'Client secret' },
validation: {
required: true,
message: 'This field is required',
},
},
clientId: {
label: 'Client Id',
type: 'text',
@ -262,6 +278,16 @@ export function fieldMap(provider: string): Record<string, FieldData> {
type: 'secret',
description: 'The client secret of your OAuth2 app.',
},
managedIdentityClientId: {
label: 'FIC managed identity client Id',
type: 'text',
description: 'The managed identity client Id of the federated identity credential of your OAuth2 app.',
},
federatedCredentialAudience: {
label: 'FIC audience',
type: 'text',
description: 'The audience of the federated identity credential of your OAuth2 app.',
},
allowedOrganizations: {
label: 'Allowed organizations',
type: 'select',
@ -652,3 +678,16 @@ function orgMappingDescription(provider: string): string {
return 'List of "<ExternalName>:<OrgIdOrName>:<Role>" mappings.';
}
}
function clientAuthenticationOptions(provider: string): Array<SelectableValue<string>> {
switch (provider) {
case 'azuread':
return [
{ value: 'client_secret_post', label: 'Client secret' },
{ value: 'managed_identity', label: 'Managed identity' },
];
// Other providers ...
default:
return [{ value: 'client_secret_post', label: 'Client secret' }];
}
}

@ -21,8 +21,11 @@ export type SSOProviderSettingsBase = {
authStyle?: string;
authUrl?: string;
autoLogin?: boolean;
clientAuthentication?: string;
clientId: string;
clientSecret: string;
managedIdentityClientId?: string;
federatedCredentialAudience?: string;
emailAttributeName?: string;
emailAttributePath?: string;
emptyScopes?: boolean;

Loading…
Cancel
Save