Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pkg/api/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ func (hs *HTTPServer) tryAutoLogin(c *contextmodel.ReqContext) bool {

for providerName, provider := range oauthInfos {
if provider.AutoLogin || hs.Cfg.OAuthAutoLogin {
redirectUrl := hs.Cfg.AppSubURL + "/login/" + providerName
redirectUrl := hs.Cfg.AppSubURL + "/login/" + providerName + "?autologin=true"
if hs.Features.IsEnabledGlobally(featuremgmt.FlagUseSessionStorageForRedirection) {
redirectUrl += hs.getRedirectToForAutoLogin(c)
}
Expand Down
16 changes: 14 additions & 2 deletions pkg/api/login_oauth.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package api

import (
"github.com/grafana/grafana/pkg/apimachinery/errutil"
// "github.com/grafana/grafana/pkg/apimachinery/errutil"
"strings"

"github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/middleware/cookies"
"github.com/grafana/grafana/pkg/services/authn"
Expand All @@ -21,7 +23,9 @@ func (hs *HTTPServer) OAuthLogin(reqCtx *contextmodel.ReqContext) {
if errorParam := reqCtx.Query("error"); errorParam != "" {
errorDesc := reqCtx.Query("error_description")
hs.log.Error("failed to login ", "error", errorParam, "errorDesc", errorDesc)
hs.redirectWithError(reqCtx, errutil.Unauthorized("oauth.login", errutil.WithPublicMessage(hs.Cfg.OAuthLoginErrorMessage)).Errorf("Login provider denied login request"))
// hs.redirectWithError(reqCtx, errutil.Unauthorized("oauth.login", errutil.WithPublicMessage(hs.Cfg.OAuthLoginErrorMessage)).Errorf("Login provider denied login request"))

reqCtx.Redirect("/login?disableAutoLogin=true")
return
}

Expand All @@ -45,6 +49,14 @@ func (hs *HTTPServer) OAuthLogin(reqCtx *contextmodel.ReqContext) {
cookies.WriteCookie(reqCtx.Resp, OauthPKCECookieName, pkce, hs.Cfg.OAuthCookieMaxAge, hs.CookieOptionsFromCfg)
}

autoLogin := reqCtx.Query("autologin")
if autoLogin == "true" {
if strings.Contains(redirect.URL, "?") {
redirect.URL += "&prompt=none"
} else {
redirect.URL += "?prompt=none"
}
}
reqCtx.Redirect(redirect.URL)
return
}
Expand Down
1 change: 1 addition & 0 deletions pkg/login/social/social.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ type OAuthInfo struct {
OrgMapping []string `mapstructure:"org_mapping"`
Scopes []string `mapstructure:"scopes" toml:"scopes"`
SignoutRedirectUrl string `mapstructure:"signout_redirect_url" toml:"signout_redirect_url"`
SignoutUrl string `mapstructure:"signout_url" toml:"signout_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"`
Expand Down
1 change: 1 addition & 0 deletions pkg/models/usertoken/user_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type UserToken struct {
UpdatedAt int64
RevokedAt int64
UnhashedToken string
IdToken string `xorm:"-" json:"-"`
}

const UrgentRotateTime = 1 * time.Minute
Expand Down
5 changes: 5 additions & 0 deletions pkg/services/auth/authimpl/auth_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
SeenAt: 0,
RevokedAt: 0,
AuthTokenSeen: false,
// IdToken: cmd.IdToken,
}

err = s.sqlStore.InTransaction(ctx, func(ctx context.Context) error {
Expand Down Expand Up @@ -123,6 +124,10 @@
var userToken auth.UserToken
err = userAuthToken.toUserToken(&userToken)

if cmd.IdToken != "" {

Check failure on line 127 in pkg/services/auth/authimpl/auth_token.go

View workflow job for this annotation

GitHub Actions / lint-go

cmd.IdToken undefined (type *"github.com/grafana/grafana/pkg/services/auth".CreateTokenCommand has no field or method IdToken)

Check failure on line 127 in pkg/services/auth/authimpl/auth_token.go

View workflow job for this annotation

GitHub Actions / lint-go

cmd.IdToken undefined (type *"github.com/grafana/grafana/pkg/services/auth".CreateTokenCommand has no field or method IdToken)
userToken.IdToken = cmd.IdToken

Check failure on line 128 in pkg/services/auth/authimpl/auth_token.go

View workflow job for this annotation

GitHub Actions / lint-go

cmd.IdToken undefined (type *"github.com/grafana/grafana/pkg/services/auth".CreateTokenCommand has no field or method IdToken) (typecheck)

Check failure on line 128 in pkg/services/auth/authimpl/auth_token.go

View workflow job for this annotation

GitHub Actions / lint-go

cmd.IdToken undefined (type *"github.com/grafana/grafana/pkg/services/auth".CreateTokenCommand has no field or method IdToken)) (typecheck)
}

return &userToken, err
}

Expand Down
2 changes: 2 additions & 0 deletions pkg/services/auth/authimpl/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type userAuthToken struct {
RevokedAt int64
UnhashedToken string `xorm:"-"`
ExternalSessionId int64
IdToken string `xorm:"-"`
}

func userAuthTokenFromUserToken(ut *auth.UserToken) (*userAuthToken, error) {
Expand Down Expand Up @@ -71,5 +72,6 @@ func (uat *userAuthToken) toUserToken(ut *auth.UserToken) error {
ut.RevokedAt = uat.RevokedAt
ut.UnhashedToken = uat.UnhashedToken
ut.ExternalSessionId = uat.ExternalSessionId
ut.IdToken = uat.IdToken
return nil
}
29 changes: 25 additions & 4 deletions pkg/services/authn/authnimpl/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import (
"context"
"errors"
"fmt"
"net/http"
"net/url"
"strconv"
"strings"

Expand Down Expand Up @@ -241,13 +243,19 @@
s.log.FromContext(ctx).Debug("Failed to parse ip from address", "client", c.Name(), "id", id.ID, "addr", addr, "error", err)
}

var sessionToken *auth.UserToken
var sessionTokenErr error
externalSession := s.resolveExternalSessionFromIdentity(ctx, id, userID)
if externalSession != nil && externalSession.IDToken != "" {
sessionToken, sessionTokenErr = s.sessionService.CreateToken(ctx, &auth.CreateTokenCommand{User: &user.User{ID: userID}, ClientIP: ip, UserAgent: r.HTTPRequest.UserAgent(), ExternalSession: externalSession, IdToken: externalSession.IDToken})

Check failure on line 250 in pkg/services/authn/authnimpl/service.go

View workflow job for this annotation

GitHub Actions / lint-go

unknown field IdToken in struct literal of type "github.com/grafana/grafana/pkg/services/auth".CreateTokenCommand (typecheck)

Check failure on line 250 in pkg/services/authn/authnimpl/service.go

View workflow job for this annotation

GitHub Actions / lint-go

unknown field IdToken in struct literal of type "github.com/grafana/grafana/pkg/services/auth".CreateTokenCommand) (typecheck)

Check failure on line 250 in pkg/services/authn/authnimpl/service.go

View workflow job for this annotation

GitHub Actions / lint-go

unknown field IdToken in struct literal of type "github.com/grafana/grafana/pkg/services/auth".CreateTokenCommand) (typecheck)
} else {
sessionToken, sessionTokenErr = s.sessionService.CreateToken(ctx, &auth.CreateTokenCommand{User: &user.User{ID: userID}, ClientIP: ip, UserAgent: r.HTTPRequest.UserAgent(), ExternalSession: externalSession})
}

sessionToken, err := s.sessionService.CreateToken(ctx, &auth.CreateTokenCommand{User: &user.User{ID: userID}, ClientIP: ip, UserAgent: r.HTTPRequest.UserAgent(), ExternalSession: externalSession})
if err != nil {
if sessionTokenErr != nil {
s.metrics.failedLogin.WithLabelValues(client).Inc()
s.log.FromContext(ctx).Error("Failed to create session", "client", client, "id", id.ID, "err", err)
return nil, err
s.log.FromContext(ctx).Error("Failed to create session", "client", client, "id", id.ID, "err", sessionTokenErr)
return nil, sessionTokenErr
}

s.metrics.successfulLogin.WithLabelValues(client).Inc()
Expand Down Expand Up @@ -327,6 +335,19 @@
goto Default
}

if s.cfg.SignoutRedirectUrl != "" {
rawRedirect := s.cfg.AppURL + "login?disableAutoLogin=true"
idToken := sessionToken.IdToken

logoutURL := fmt.Sprintf(
"%s?post_logout_redirect_uri=%s&id_token_hint=%s",
s.cfg.SignoutUrl,
url.QueryEscape(rawRedirect),
idToken,
)
clientRedirect.URL = logoutURL
}

redirect = clientRedirect
}

Expand Down
1 change: 1 addition & 0 deletions pkg/services/ssosettings/strategies/oauth_strategy.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ func (s *OAuthStrategy) loadSettingsForProvider(provider string) map[string]any
"auto_login": section.Key("auto_login").MustBool(false),
"allowed_groups": section.Key("allowed_groups").Value(),
"signout_redirect_url": section.Key("signout_redirect_url").Value(),
"signout_url": section.Key("signout_url").Value(),
"org_mapping": section.Key("org_mapping").Value(),
"org_attribute_path": section.Key("org_attribute_path").Value(),
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/setting/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ type Cfg struct {
AdminEmail string
DisableLoginForm bool
SignoutRedirectUrl string
SignoutUrl string
IDResponseHeaderEnabled bool
IDResponseHeaderPrefix string
IDResponseHeaderNamespaces map[string]struct{}
Expand Down Expand Up @@ -1629,6 +1630,7 @@ func readAuthSettings(iniFile *ini.File, cfg *Cfg) (err error) {
cfg.OAuthCookieMaxAge = auth.Key("oauth_state_cookie_max_age").MustInt(600)
cfg.OAuthRefreshTokenServerLockMinWaitMs = auth.Key("oauth_refresh_token_server_lock_min_wait_ms").MustInt64(1000)
cfg.SignoutRedirectUrl = valueAsString(auth, "signout_redirect_url", "")
cfg.SignoutUrl = valueAsString(auth, "signout_url", "")

// Deprecated
cfg.OAuthSkipOrgRoleUpdateSync = false
Expand Down
Loading