Auth: Split signout_redirect_url into per provider settings (#75269)

* Split signout_redirect_url into per provider settings

* Split signout_redirect_url into per provider settings

* Update docs/sources/setup-grafana/configure-security/configure-authentication/grafana/index.md

Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com>

* Split signout_redirect_url into per provider settings

* Split signout_redirect_url into per provider settings

* Split signout_redirect_url into per provider settings

* Split signout_redirect_url into per provider settings

* Split signout_redirect_url into per provider settings

* Split signout_redirect_url into per provider settings

* update docs

* update devenvs

* add missing struct tag

---------

Co-authored-by: Rao, B V Chalapathi <b_v_chalapathi.rao@nokia.com>
Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com>
Co-authored-by: jguer <me@jguer.space>
This commit is contained in:
venkatbvc
2023-11-29 19:20:21 +05:30
committed by GitHub
parent 73776f37eb
commit e152323a33
15 changed files with 128 additions and 24 deletions

View File

@ -592,6 +592,7 @@ scopes = user:email,read:org
auth_url = https://github.com/login/oauth/authorize auth_url = https://github.com/login/oauth/authorize
token_url = https://github.com/login/oauth/access_token token_url = https://github.com/login/oauth/access_token
api_url = https://api.github.com/user api_url = https://api.github.com/user
signout_redirect_url =
allowed_domains = allowed_domains =
team_ids = team_ids =
allowed_organizations = allowed_organizations =
@ -619,6 +620,7 @@ scopes = openid email profile
auth_url = https://gitlab.com/oauth/authorize auth_url = https://gitlab.com/oauth/authorize
token_url = https://gitlab.com/oauth/token token_url = https://gitlab.com/oauth/token
api_url = https://gitlab.com/api/v4 api_url = https://gitlab.com/api/v4
signout_redirect_url =
allowed_domains = allowed_domains =
allowed_groups = allowed_groups =
role_attribute_path = role_attribute_path =
@ -645,6 +647,7 @@ scopes = openid email profile
auth_url = https://accounts.google.com/o/oauth2/v2/auth auth_url = https://accounts.google.com/o/oauth2/v2/auth
token_url = https://oauth2.googleapis.com/token token_url = https://oauth2.googleapis.com/token
api_url = https://openidconnect.googleapis.com/v1/userinfo api_url = https://openidconnect.googleapis.com/v1/userinfo
signout_redirect_url =
allowed_domains = allowed_domains =
hosted_domain = hosted_domain =
allowed_groups = allowed_groups =
@ -695,6 +698,7 @@ client_secret =
scopes = openid email profile scopes = openid email profile
auth_url = https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/authorize auth_url = https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/authorize
token_url = https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token token_url = https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token
signout_redirect_url =
allowed_domains = allowed_domains =
allowed_groups = allowed_groups =
allowed_organizations = allowed_organizations =
@ -722,6 +726,7 @@ scopes = openid profile email groups
auth_url = https://<tenant-id>.okta.com/oauth2/v1/authorize auth_url = https://<tenant-id>.okta.com/oauth2/v1/authorize
token_url = https://<tenant-id>.okta.com/oauth2/v1/token token_url = https://<tenant-id>.okta.com/oauth2/v1/token
api_url = https://<tenant-id>.okta.com/oauth2/v1/userinfo api_url = https://<tenant-id>.okta.com/oauth2/v1/userinfo
signout_redirect_url =
allowed_domains = allowed_domains =
allowed_groups = allowed_groups =
role_attribute_path = role_attribute_path =
@ -758,6 +763,7 @@ team_ids_attribute_path =
auth_url = auth_url =
token_url = token_url =
api_url = api_url =
signout_redirect_url =
teams_url = teams_url =
allowed_domains = allowed_domains =
allowed_groups = allowed_groups =
@ -808,6 +814,7 @@ auto_sign_up = false
url_login = false url_login = false
allow_assign_grafana_admin = false allow_assign_grafana_admin = false
skip_org_role_sync = false skip_org_role_sync = false
signout_redirect_url =
#################################### Auth LDAP ########################### #################################### Auth LDAP ###########################
[auth.ldap] [auth.ldap]

View File

@ -581,6 +581,7 @@
;auth_url = https://github.com/login/oauth/authorize ;auth_url = https://github.com/login/oauth/authorize
;token_url = https://github.com/login/oauth/access_token ;token_url = https://github.com/login/oauth/access_token
;api_url = https://api.github.com/user ;api_url = https://api.github.com/user
;signout_redirect_url =
;allowed_domains = ;allowed_domains =
;team_ids = ;team_ids =
;allowed_organizations = ;allowed_organizations =
@ -602,6 +603,7 @@
;auth_url = https://gitlab.com/oauth/authorize ;auth_url = https://gitlab.com/oauth/authorize
;token_url = https://gitlab.com/oauth/token ;token_url = https://gitlab.com/oauth/token
;api_url = https://gitlab.com/api/v4 ;api_url = https://gitlab.com/api/v4
;signout_redirect_url =
;allowed_domains = ;allowed_domains =
;allowed_groups = ;allowed_groups =
;role_attribute_path = ;role_attribute_path =
@ -627,6 +629,7 @@
;auth_url = https://accounts.google.com/o/oauth2/v2/auth ;auth_url = https://accounts.google.com/o/oauth2/v2/auth
;token_url = https://oauth2.googleapis.com/token ;token_url = https://oauth2.googleapis.com/token
;api_url = https://openidconnect.googleapis.com/v1/userinfo ;api_url = https://openidconnect.googleapis.com/v1/userinfo
;signout_redirect_url =
;allowed_domains = ;allowed_domains =
;hosted_domain = ;hosted_domain =
;allowed_groups = ;allowed_groups =
@ -661,6 +664,7 @@
;scopes = openid email profile ;scopes = openid email profile
;auth_url = https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/authorize ;auth_url = https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/authorize
;token_url = https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token ;token_url = https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token
;signout_redirect_url =
;allowed_domains = ;allowed_domains =
;allowed_groups = ;allowed_groups =
;allowed_organizations = ;allowed_organizations =
@ -682,6 +686,7 @@
;auth_url = https://<tenant-id>.okta.com/oauth2/v1/authorize ;auth_url = https://<tenant-id>.okta.com/oauth2/v1/authorize
;token_url = https://<tenant-id>.okta.com/oauth2/v1/token ;token_url = https://<tenant-id>.okta.com/oauth2/v1/token
;api_url = https://<tenant-id>.okta.com/oauth2/v1/userinfo ;api_url = https://<tenant-id>.okta.com/oauth2/v1/userinfo
;signout_redirect_url =
;allowed_domains = ;allowed_domains =
;allowed_groups = ;allowed_groups =
;role_attribute_path = ;role_attribute_path =
@ -708,6 +713,7 @@
;auth_url = https://foo.bar/login/oauth/authorize ;auth_url = https://foo.bar/login/oauth/authorize
;token_url = https://foo.bar/login/oauth/access_token ;token_url = https://foo.bar/login/oauth/access_token
;api_url = https://foo.bar/user ;api_url = https://foo.bar/user
;signout_redirect_url =
;teams_url = ;teams_url =
;allowed_domains = ;allowed_domains =
;team_ids = ;team_ids =

View File

@ -85,8 +85,6 @@ auth_url = http://localhost:9000/application/o/authorize/
token_url = http://localhost:9000/application/o/token/ token_url = http://localhost:9000/application/o/token/
api_url = http://localhost:9000/application/o/userinfo/ api_url = http://localhost:9000/application/o/userinfo/
role_attribute_path = contains(groups[*], 'admin') && 'Admin' || contains(groups[*], 'editor') && 'Editor' || 'Viewer' role_attribute_path = contains(groups[*], 'admin') && 'Admin' || contains(groups[*], 'editor') && 'Editor' || 'Viewer'
[auth]
signout_redirect_url = http://localhost:9000/application/o/grafana-oidc/end-session/ signout_redirect_url = http://localhost:9000/application/o/grafana-oidc/end-session/
``` ```

View File

@ -1,5 +1,5 @@
authentikdb: authentikdb:
image: docker.io/library/postgres:12-alpine image: docker.io/library/postgres:16-alpine
restart: unless-stopped restart: unless-stopped
container_name: authentikdb container_name: authentikdb
environment: environment:
@ -39,7 +39,7 @@
- "authentik:authentik" - "authentik:authentik"
authentik: authentik:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.5.1} image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.10.4}
restart: unless-stopped restart: unless-stopped
container_name: authentik container_name: authentik
command: server command: server
@ -66,7 +66,7 @@
- "authentikredis:authentikredis" - "authentikredis:authentikredis"
authentik-worker: authentik-worker:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.5.1} image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.10.4}
restart: unless-stopped restart: unless-stopped
container_name: authentik-worker container_name: authentik-worker
command: worker command: worker

View File

@ -1,5 +1,5 @@
oauthkeycloakdb: oauthkeycloakdb:
image: docker.io/library/postgres:12-alpine image: docker.io/library/postgres:16-alpine
container_name: oauthkeycloakdb container_name: oauthkeycloakdb
environment: environment:
POSTGRES_DB: keycloak POSTGRES_DB: keycloak
@ -10,7 +10,7 @@
restart: unless-stopped restart: unless-stopped
oauthkeycloak: oauthkeycloak:
image: quay.io/keycloak/keycloak:21.1 image: quay.io/keycloak/keycloak:23.0
container_name: oauthkeycloak container_name: oauthkeycloak
command: start-dev command: start-dev
environment: environment:

View File

@ -1,5 +1,5 @@
oauthkeycloakdb: oauthkeycloakdb:
image: docker.io/library/postgres:12-alpine image: docker.io/library/postgres:16-alpine
container_name: oauthkeycloakdb container_name: oauthkeycloakdb
environment: environment:
POSTGRES_DB: keycloak POSTGRES_DB: keycloak
@ -10,7 +10,7 @@
restart: unless-stopped restart: unless-stopped
oauthkeycloak: oauthkeycloak:
image: quay.io/keycloak/keycloak:22.0 image: quay.io/keycloak/keycloak:23.0
container_name: oauthkeycloak container_name: oauthkeycloak
command: start-dev command: start-dev
environment: environment:

View File

@ -10,9 +10,6 @@ make devenv sources="auth/oauth"
Here is the conf you need to add to your configuration file (conf/custom.ini): Here is the conf you need to add to your configuration file (conf/custom.ini):
```ini ```ini
[auth]
signout_redirect_url = http://localhost:8087/realms/grafana/protocol/openid-connect/logout?post_logout_redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Flogin
[auth.generic_oauth] [auth.generic_oauth]
enabled = true enabled = true
name = Keycloak-OAuth name = Keycloak-OAuth
@ -28,6 +25,7 @@ auth_url = http://localhost:8087/realms/grafana/protocol/openid-connect/auth
token_url = http://localhost:8087/realms/grafana/protocol/openid-connect/token token_url = http://localhost:8087/realms/grafana/protocol/openid-connect/token
role_attribute_path = contains(roles[*], 'grafanaadmin') && 'GrafanaAdmin' || contains(roles[*], 'admin') && 'Admin' || contains(roles[*], 'editor') && 'Editor' || 'Viewer' role_attribute_path = contains(roles[*], 'grafanaadmin') && 'GrafanaAdmin' || contains(roles[*], 'admin') && 'Admin' || contains(roles[*], 'editor') && 'Editor' || 'Viewer'
allow_assign_grafana_admin = true allow_assign_grafana_admin = true
signout_redirect_url = http://localhost:8087/realms/grafana/protocol/openid-connect/logout?post_logout_redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Flogin
``` ```
## Devenv setup jwt auth ## Devenv setup jwt auth

View File

@ -205,10 +205,12 @@ disable_signout_menu = true
### URL redirect after signing out ### URL redirect after signing out
URL to redirect the user to after signing out from Grafana. This can for example be used to enable signout from OAuth provider. URL to redirect the user to after signing out from Grafana. This can for example be used to enable signout from an OAuth provider.
Example for Generic OAuth:
```bash ```bash
[auth] [auth.generic_oauth]
signout_redirect_url = signout_redirect_url =
``` ```

View File

@ -121,9 +121,12 @@ disable_signout_menu = true
### URL redirect after signing out ### URL redirect after signing out
URL to redirect the user to after signing out from Grafana. This can for example be used to enable signout from oauth provider. The URL to redirect the user to after signing out from Grafana can be configured under `[auth]` or under a specific OAuth provider section (for example, `[auth.generic_oauth]`). The URL configured under a specific OAuth provider section takes precedence over the URL configured in `[auth]` section. This can, for example, enable signout from the OAuth provider.
```bash ```bash
[auth.generic_oauth]
signout_redirect_url =
[auth] [auth]
signout_redirect_url = signout_redirect_url =
``` ```

View File

@ -136,7 +136,7 @@ groups_attribute_path = reverse("Global:department")
To enable Single Logout, you need to add the following option to the configuration of Grafana: To enable Single Logout, you need to add the following option to the configuration of Grafana:
```ini ```ini
[auth] [auth.generic_oauth]
signout_redirect_url = https://<PROVIDER_DOMAIN>/auth/realms/<REALM_NAME>/protocol/openid-connect/logout?post_logout_redirect_uri=https%3A%2F%2<GRAFANA_DOMAIN>%2Flogin signout_redirect_url = https://<PROVIDER_DOMAIN>/auth/realms/<REALM_NAME>/protocol/openid-connect/logout?post_logout_redirect_uri=https%3A%2F%2<GRAFANA_DOMAIN>%2Flogin
``` ```

View File

@ -11,6 +11,7 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/api/response"
@ -214,6 +215,40 @@ func setupScenarioContext(t *testing.T, url string) *scenarioContext {
return sc return sc
} }
func setupScenarioContextSamlLogout(t *testing.T, url string) *scenarioContext {
cfg := setting.NewCfg()
//seed sections and keys
cfg.Raw.DeleteSection("DEFAULT")
saml, err := cfg.Raw.NewSection("auth.saml")
assert.NoError(t, err)
_, err = saml.NewKey("enabled", "true")
assert.NoError(t, err)
_, err = saml.NewKey("allow_idp_initiated", "false")
assert.NoError(t, err)
_, err = saml.NewKey("single_logout", "true")
assert.NoError(t, err)
ctxHdlr := getContextHandler(t, cfg)
sc := &scenarioContext{
url: url,
t: t,
cfg: cfg,
ctxHdlr: ctxHdlr,
}
viewsPath, err := filepath.Abs("../../public/views")
require.NoError(t, err)
exists, err := fs.Exists(viewsPath)
require.NoError(t, err)
require.Truef(t, exists, "Views should be in %q", viewsPath)
sc.m = web.New()
sc.m.UseMiddleware(web.Renderer(viewsPath, "[[", "]]"))
sc.m.Use(ctxHdlr.Middleware)
return sc
}
// FIXME: This user should not be anonymous
func authedUserWithPermissions(userID, orgID int64, permissions []accesscontrol.Permission) *user.SignedInUser { func authedUserWithPermissions(userID, orgID int64, permissions []accesscontrol.Permission) *user.SignedInUser {
return &user.SignedInUser{UserID: userID, OrgID: orgID, OrgRole: org.RoleViewer, Permissions: map[int64]map[string][]string{orgID: accesscontrol.GroupScopesByAction(permissions)}} return &user.SignedInUser{UserID: userID, OrgID: orgID, OrgRole: org.RoleViewer, Permissions: map[int64]map[string][]string{orgID: accesscontrol.GroupScopesByAction(permissions)}}
} }

View File

@ -248,19 +248,29 @@ func (hs *HTTPServer) Logout(c *contextmodel.ReqContext) {
hs.log.Error("failed to retrieve user ID", "error", errID) hs.log.Error("failed to retrieve user ID", "error", errID)
} }
oauthProviderSignoutRedirectUrl := ""
getAuthQuery := loginservice.GetAuthInfoQuery{UserId: userID}
authInfo, err := hs.authInfoService.GetAuthInfo(c.Req.Context(), &getAuthQuery)
if err == nil {
// If SAML is enabled and this is a SAML user use saml logout // If SAML is enabled and this is a SAML user use saml logout
if hs.samlSingleLogoutEnabled() { if hs.samlSingleLogoutEnabled() {
getAuthQuery := loginservice.GetAuthInfoQuery{UserId: userID}
if authInfo, err := hs.authInfoService.GetAuthInfo(c.Req.Context(), &getAuthQuery); err == nil {
if authInfo.AuthModule == loginservice.SAMLAuthModule { if authInfo.AuthModule == loginservice.SAMLAuthModule {
c.Redirect(hs.Cfg.AppSubURL + "/logout/saml") c.Redirect(hs.Cfg.AppSubURL + "/logout/saml")
return return
} }
} }
oauthProvider := hs.SocialService.GetOAuthInfoProvider(strings.TrimPrefix(authInfo.AuthModule, "oauth_"))
oauthProviderSignoutRedirectUrl = oauthProvider.SignoutRedirectUrl
} }
hs.log.Debug("Logout Redirect url", "auth.SignoutRedirectUrl:", hs.Cfg.SignoutRedirectUrl)
hs.log.Debug("Logout Redirect url", "oauth provider redirect url:", oauthProviderSignoutRedirectUrl)
signOutRedirectUrl := getSignOutRedirectUrl(hs.Cfg.SignoutRedirectUrl, oauthProviderSignoutRedirectUrl)
hs.log.Debug("Logout Redirect url", "signOurRedirectUrl:", signOutRedirectUrl)
idTokenHint := "" idTokenHint := ""
oidcLogout := isPostLogoutRedirectConfigured(hs.Cfg.SignoutRedirectUrl) oidcLogout := isPostLogoutRedirectConfigured(signOutRedirectUrl)
// Invalidate the OAuth tokens in case the User logged in with OAuth or the last external AuthEntry is an OAuth one // Invalidate the OAuth tokens in case the User logged in with OAuth or the last external AuthEntry is an OAuth one
if entry, exists, _ := hs.oauthTokenService.HasOAuthEntry(c.Req.Context(), c.SignedInUser); exists { if entry, exists, _ := hs.oauthTokenService.HasOAuthEntry(c.Req.Context(), c.SignedInUser); exists {
@ -278,17 +288,17 @@ func (hs *HTTPServer) Logout(c *contextmodel.ReqContext) {
} }
} }
err := hs.AuthTokenService.RevokeToken(c.Req.Context(), c.UserToken, false) err = hs.AuthTokenService.RevokeToken(c.Req.Context(), c.UserToken, false)
if err != nil && !errors.Is(err, auth.ErrUserTokenNotFound) { if err != nil && !errors.Is(err, auth.ErrUserTokenNotFound) {
hs.log.Error("failed to revoke auth token", "error", err) hs.log.Error("failed to revoke auth token", "error", err)
} }
authn.DeleteSessionCookie(c.Resp, hs.Cfg) authn.DeleteSessionCookie(c.Resp, hs.Cfg)
rdUrl := hs.Cfg.SignoutRedirectUrl rdUrl := signOutRedirectUrl
if rdUrl != "" { if rdUrl != "" {
if oidcLogout { if oidcLogout {
rdUrl = getPostRedirectUrl(hs.Cfg.SignoutRedirectUrl, idTokenHint) rdUrl = getPostRedirectUrl(signOutRedirectUrl, idTokenHint)
} }
c.Redirect(rdUrl) c.Redirect(rdUrl)
} else { } else {
@ -443,3 +453,12 @@ func getPostRedirectUrl(rdUrl string, tokenHint string) string {
return u.String() return u.String()
} }
func getSignOutRedirectUrl(gRdUrl string, oauthProviderUrl string) string {
if oauthProviderUrl != "" {
return oauthProviderUrl
} else if gRdUrl != "" {
return gRdUrl
}
return ""
}

View File

@ -29,7 +29,9 @@ import (
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/hooks" "github.com/grafana/grafana/pkg/services/hooks"
"github.com/grafana/grafana/pkg/services/licensing" "github.com/grafana/grafana/pkg/services/licensing"
"github.com/grafana/grafana/pkg/services/licensing/licensingtest"
loginservice "github.com/grafana/grafana/pkg/services/login" loginservice "github.com/grafana/grafana/pkg/services/login"
"github.com/grafana/grafana/pkg/services/login/authinfotest"
"github.com/grafana/grafana/pkg/services/navtree" "github.com/grafana/grafana/pkg/services/navtree"
"github.com/grafana/grafana/pkg/services/secrets" "github.com/grafana/grafana/pkg/services/secrets"
"github.com/grafana/grafana/pkg/services/secrets/fakes" "github.com/grafana/grafana/pkg/services/secrets/fakes"
@ -644,6 +646,37 @@ func setupAuthProxyLoginTest(t *testing.T, enableLoginToken bool) *scenarioConte
return sc return sc
} }
func TestLogoutSaml(t *testing.T) {
fakeSetIndexViewData(t)
fakeViewIndex(t)
sc := setupScenarioContextSamlLogout(t, "/logout")
license := licensingtest.NewFakeLicensing()
license.On("FeatureEnabled", "saml").Return(true)
hs := &HTTPServer{
Cfg: sc.cfg,
SettingsProvider: &setting.OSSImpl{Cfg: sc.cfg},
License: license,
SocialService: &mockSocialService{},
Features: featuremgmt.WithFeatures(),
authInfoService: &authinfotest.FakeService{
ExpectedUserAuth: &loginservice.UserAuth{AuthModule: loginservice.SAMLAuthModule},
},
}
assert.Equal(t, true, hs.samlSingleLogoutEnabled())
sc.defaultHandler = routing.Wrap(func(c *contextmodel.ReqContext) response.Response {
c.SignedInUser = &user.SignedInUser{
UserID: 1,
}
hs.Logout(c)
return response.Empty(http.StatusOK)
})
sc.m.Get(sc.url, sc.defaultHandler)
sc.fakeReqNoAssertions("GET", sc.url).exec()
require.Equal(t, 302, sc.resp.Code)
}
type mockSocialService struct { type mockSocialService struct {
oAuthInfo *social.OAuthInfo oAuthInfo *social.OAuthInfo
oAuthInfos map[string]*social.OAuthInfo oAuthInfos map[string]*social.OAuthInfo

View File

@ -47,6 +47,7 @@ skip_org_role_sync = true
use_refresh_token = true use_refresh_token = true
empty_scopes = empty_scopes =
hosted_domain = test_hosted_domain hosted_domain = test_hosted_domain
signout_redirect_url = https://oauth.com/signout?post_logout_redirect_uri=https://grafana.com
` `
iniFile, err := ini.Load([]byte(iniContent)) iniFile, err := ini.Load([]byte(iniContent))
@ -83,6 +84,7 @@ hosted_domain = test_hosted_domain
AllowAssignGrafanaAdmin: true, AllowAssignGrafanaAdmin: true,
UseRefreshToken: true, UseRefreshToken: true,
HostedDomain: "test_hosted_domain", HostedDomain: "test_hosted_domain",
SignoutRedirectUrl: "https://oauth.com/signout?post_logout_redirect_uri=https://grafana.com",
Extra: map[string]string{ Extra: map[string]string{
"allowed_organizations": "org1, org2", "allowed_organizations": "org1, org2",
"id_token_attribute_name": "id_token", "id_token_attribute_name": "id_token",

View File

@ -74,6 +74,7 @@ type OAuthInfo struct {
TlsSkipVerify bool `mapstructure:"tls_skip_verify_insecure" toml:"tls_skip_verify_insecure"` TlsSkipVerify bool `mapstructure:"tls_skip_verify_insecure" toml:"tls_skip_verify_insecure"`
UsePKCE bool `mapstructure:"use_pkce" toml:"use_pkce"` UsePKCE bool `mapstructure:"use_pkce" toml:"use_pkce"`
UseRefreshToken bool `mapstructure:"use_refresh_token" toml:"use_refresh_token"` UseRefreshToken bool `mapstructure:"use_refresh_token" toml:"use_refresh_token"`
SignoutRedirectUrl string `mapstructure:"signout_redirect_url" toml:"signout_redirect_url"`
Extra map[string]string `mapstructure:",remain" toml:"extra,omitempty"` Extra map[string]string `mapstructure:",remain" toml:"extra,omitempty"`
} }