Authn: Handle logout logic in auth broker (#79635)
* AuthN: Add new client extension interface that allows for custom logout logic * AuthN: Add tests for oauth client logout * Call authn.Logout Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com>
This commit is contained in:
co-authored by
Gabriel MABILLE
parent
eb490193b9
commit
8cb351e54a
+17
-102
@@ -29,8 +29,6 @@ import (
|
||||
const (
|
||||
viewIndex = "index"
|
||||
loginErrorCookieName = "login_error"
|
||||
// #nosec G101 - this is not a hardcoded secret
|
||||
postLogoutRedirectParam = "post_logout_redirect_uri"
|
||||
)
|
||||
|
||||
var setIndexViewData = (*HTTPServer).setIndexViewData
|
||||
@@ -243,70 +241,31 @@ func (hs *HTTPServer) loginUserWithUser(user *user.User, c *contextmodel.ReqCont
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) Logout(c *contextmodel.ReqContext) {
|
||||
userID, errID := identity.UserIdentifier(c.SignedInUser.GetNamespacedID())
|
||||
if errID != nil {
|
||||
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 hs.samlSingleLogoutEnabled() {
|
||||
if authInfo.AuthModule == loginservice.SAMLAuthModule {
|
||||
c.Redirect(hs.Cfg.AppSubURL + "/logout/saml")
|
||||
return
|
||||
}
|
||||
// FIXME: restructure saml client to implement authn.LogoutClient
|
||||
if hs.samlSingleLogoutEnabled() {
|
||||
id, err := identity.UserIdentifier(c.SignedInUser.GetNamespacedID())
|
||||
if err != nil {
|
||||
hs.log.Error("failed to retrieve user ID", "error", err)
|
||||
}
|
||||
oauthProvider := hs.SocialService.GetOAuthInfoProvider(strings.TrimPrefix(authInfo.AuthModule, "oauth_"))
|
||||
if oauthProvider != nil {
|
||||
oauthProviderSignoutRedirectUrl = oauthProvider.SignoutRedirectUrl
|
||||
|
||||
authInfo, _ := hs.authInfoService.GetAuthInfo(c.Req.Context(), &loginservice.GetAuthInfoQuery{UserId: id})
|
||||
if authInfo != nil && authInfo.AuthModule == loginservice.SAMLAuthModule {
|
||||
c.Redirect(hs.Cfg.AppSubURL + "/logout/saml")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
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 := ""
|
||||
oidcLogout := isPostLogoutRedirectConfigured(signOutRedirectUrl)
|
||||
|
||||
// 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 {
|
||||
token := hs.oauthTokenService.GetCurrentOAuthToken(c.Req.Context(), c.SignedInUser)
|
||||
if oidcLogout {
|
||||
if token.Valid() {
|
||||
idTokenHint = token.Extra("id_token").(string)
|
||||
} else {
|
||||
hs.log.Warn("Token is not valid")
|
||||
}
|
||||
}
|
||||
|
||||
if err := hs.oauthTokenService.InvalidateOAuthTokens(c.Req.Context(), entry); err != nil {
|
||||
hs.log.Warn("failed to invalidate oauth tokens for user", "userId", userID, "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
err = hs.AuthTokenService.RevokeToken(c.Req.Context(), c.UserToken, false)
|
||||
if err != nil && !errors.Is(err, auth.ErrUserTokenNotFound) {
|
||||
hs.log.Error("failed to revoke auth token", "error", err)
|
||||
}
|
||||
|
||||
redirect, err := hs.authnService.Logout(c.Req.Context(), c.SignedInUser, c.UserToken)
|
||||
authn.DeleteSessionCookie(c.Resp, hs.Cfg)
|
||||
|
||||
rdUrl := signOutRedirectUrl
|
||||
if rdUrl != "" {
|
||||
if oidcLogout {
|
||||
rdUrl = getPostRedirectUrl(signOutRedirectUrl, idTokenHint)
|
||||
}
|
||||
c.Redirect(rdUrl)
|
||||
} else {
|
||||
hs.log.Info("Successful Logout", "User", c.SignedInUser.GetEmail())
|
||||
if err != nil {
|
||||
hs.log.Error("Failed perform proper logout", "error", err)
|
||||
c.Redirect(hs.Cfg.AppSubURL + "/login")
|
||||
}
|
||||
|
||||
_, id := c.SignedInUser.GetNamespacedID()
|
||||
hs.log.Info("Successful Logout", "userID", id)
|
||||
c.Redirect(redirect.URL)
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) tryGetEncryptedCookie(ctx *contextmodel.ReqContext, cookieName string) (string, bool) {
|
||||
@@ -420,47 +379,3 @@ func getFirstPublicErrorMessage(err *errutil.Error) string {
|
||||
|
||||
return errPublic.Message
|
||||
}
|
||||
|
||||
func isPostLogoutRedirectConfigured(redirectUrl string) bool {
|
||||
if redirectUrl == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
u, err := url.Parse(redirectUrl)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
q := u.Query()
|
||||
_, ok := q[postLogoutRedirectParam]
|
||||
return ok
|
||||
}
|
||||
|
||||
func getPostRedirectUrl(rdUrl string, tokenHint string) string {
|
||||
if tokenHint == "" {
|
||||
return rdUrl
|
||||
}
|
||||
if rdUrl == "" {
|
||||
return rdUrl
|
||||
}
|
||||
|
||||
u, err := url.Parse(rdUrl)
|
||||
if err != nil {
|
||||
return rdUrl
|
||||
}
|
||||
|
||||
q := u.Query()
|
||||
q.Set("id_token_hint", tokenHint)
|
||||
u.RawQuery = q.Encode()
|
||||
|
||||
return u.String()
|
||||
}
|
||||
|
||||
func getSignOutRedirectUrl(gRdUrl string, oauthProviderUrl string) string {
|
||||
if oauthProviderUrl != "" {
|
||||
return oauthProviderUrl
|
||||
} else if gRdUrl != "" {
|
||||
return gRdUrl
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user