mirror of
https://github.com/grafana/grafana.git
synced 2026-08-12 06:05:02 -05:00
Auth: Add request context to UserInfo calls (#70007)
* use context for UserInfo requests * set timeouts for oauth http client * Update pkg/login/social/common.go Co-authored-by: Ieva <ieva.vasiljeva@grafana.com> --------- Co-authored-by: Ieva <ieva.vasiljeva@grafana.com>
This commit is contained in:
+49
-48
@@ -70,61 +70,61 @@ func genPKCECode() (string, string, error) {
|
||||
return string(ascii), pkce, nil
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) OAuthLogin(ctx *contextmodel.ReqContext) {
|
||||
name := web.Params(ctx.Req)[":name"]
|
||||
func (hs *HTTPServer) OAuthLogin(reqCtx *contextmodel.ReqContext) {
|
||||
name := web.Params(reqCtx.Req)[":name"]
|
||||
loginInfo := loginservice.LoginInfo{AuthModule: name}
|
||||
|
||||
if errorParam := ctx.Query("error"); errorParam != "" {
|
||||
errorDesc := ctx.Query("error_description")
|
||||
if errorParam := reqCtx.Query("error"); errorParam != "" {
|
||||
errorDesc := reqCtx.Query("error_description")
|
||||
oauthLogger.Error("failed to login ", "error", errorParam, "errorDesc", errorDesc)
|
||||
hs.handleOAuthLoginErrorWithRedirect(ctx, loginInfo, login.ErrProviderDeniedRequest, "error", errorParam, "errorDesc", errorDesc)
|
||||
hs.handleOAuthLoginErrorWithRedirect(reqCtx, loginInfo, login.ErrProviderDeniedRequest, "error", errorParam, "errorDesc", errorDesc)
|
||||
return
|
||||
}
|
||||
|
||||
code := ctx.Query("code")
|
||||
code := reqCtx.Query("code")
|
||||
|
||||
if hs.Cfg.AuthBrokerEnabled {
|
||||
req := &authn.Request{HTTPRequest: ctx.Req, Resp: ctx.Resp}
|
||||
req := &authn.Request{HTTPRequest: reqCtx.Req, Resp: reqCtx.Resp}
|
||||
if code == "" {
|
||||
redirect, err := hs.authnService.RedirectURL(ctx.Req.Context(), authn.ClientWithPrefix(name), req)
|
||||
redirect, err := hs.authnService.RedirectURL(reqCtx.Req.Context(), authn.ClientWithPrefix(name), req)
|
||||
if err != nil {
|
||||
ctx.Redirect(hs.redirectURLWithErrorCookie(ctx, err))
|
||||
reqCtx.Redirect(hs.redirectURLWithErrorCookie(reqCtx, err))
|
||||
return
|
||||
}
|
||||
|
||||
if pkce := redirect.Extra[authn.KeyOAuthPKCE]; pkce != "" {
|
||||
cookies.WriteCookie(ctx.Resp, OauthPKCECookieName, pkce, hs.Cfg.OAuthCookieMaxAge, hs.CookieOptionsFromCfg)
|
||||
cookies.WriteCookie(reqCtx.Resp, OauthPKCECookieName, pkce, hs.Cfg.OAuthCookieMaxAge, hs.CookieOptionsFromCfg)
|
||||
}
|
||||
|
||||
cookies.WriteCookie(ctx.Resp, OauthStateCookieName, redirect.Extra[authn.KeyOAuthState], hs.Cfg.OAuthCookieMaxAge, hs.CookieOptionsFromCfg)
|
||||
ctx.Redirect(redirect.URL)
|
||||
cookies.WriteCookie(reqCtx.Resp, OauthStateCookieName, redirect.Extra[authn.KeyOAuthState], hs.Cfg.OAuthCookieMaxAge, hs.CookieOptionsFromCfg)
|
||||
reqCtx.Redirect(redirect.URL)
|
||||
return
|
||||
}
|
||||
|
||||
identity, err := hs.authnService.Login(ctx.Req.Context(), authn.ClientWithPrefix(name), req)
|
||||
identity, err := hs.authnService.Login(reqCtx.Req.Context(), authn.ClientWithPrefix(name), req)
|
||||
// NOTE: always delete these cookies, even if login failed
|
||||
cookies.DeleteCookie(ctx.Resp, OauthPKCECookieName, hs.CookieOptionsFromCfg)
|
||||
cookies.DeleteCookie(ctx.Resp, OauthStateCookieName, hs.CookieOptionsFromCfg)
|
||||
cookies.DeleteCookie(reqCtx.Resp, OauthPKCECookieName, hs.CookieOptionsFromCfg)
|
||||
cookies.DeleteCookie(reqCtx.Resp, OauthStateCookieName, hs.CookieOptionsFromCfg)
|
||||
|
||||
if err != nil {
|
||||
ctx.Redirect(hs.redirectURLWithErrorCookie(ctx, err))
|
||||
reqCtx.Redirect(hs.redirectURLWithErrorCookie(reqCtx, err))
|
||||
return
|
||||
}
|
||||
|
||||
metrics.MApiLoginOAuth.Inc()
|
||||
authn.HandleLoginRedirect(ctx.Req, ctx.Resp, hs.Cfg, identity, hs.ValidateRedirectTo)
|
||||
authn.HandleLoginRedirect(reqCtx.Req, reqCtx.Resp, hs.Cfg, identity, hs.ValidateRedirectTo)
|
||||
return
|
||||
}
|
||||
|
||||
provider := hs.SocialService.GetOAuthInfoProvider(name)
|
||||
if provider == nil {
|
||||
hs.handleOAuthLoginErrorWithRedirect(ctx, loginInfo, errors.New("OAuth not enabled"))
|
||||
hs.handleOAuthLoginErrorWithRedirect(reqCtx, loginInfo, errors.New("OAuth not enabled"))
|
||||
return
|
||||
}
|
||||
|
||||
connect, err := hs.SocialService.GetConnector(name)
|
||||
if err != nil {
|
||||
hs.handleOAuthLoginErrorWithRedirect(ctx, loginInfo, fmt.Errorf("no OAuth with name %s configured", name))
|
||||
hs.handleOAuthLoginErrorWithRedirect(reqCtx, loginInfo, fmt.Errorf("no OAuth with name %s configured", name))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -133,15 +133,15 @@ func (hs *HTTPServer) OAuthLogin(ctx *contextmodel.ReqContext) {
|
||||
if provider.UsePKCE {
|
||||
ascii, pkce, err := genPKCECode()
|
||||
if err != nil {
|
||||
ctx.Logger.Error("Generating PKCE failed", "error", err)
|
||||
hs.handleOAuthLoginError(ctx, loginInfo, LoginError{
|
||||
reqCtx.Logger.Error("Generating PKCE failed", "error", err)
|
||||
hs.handleOAuthLoginError(reqCtx, loginInfo, LoginError{
|
||||
HttpStatus: http.StatusInternalServerError,
|
||||
PublicMessage: "An internal error occurred",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
cookies.WriteCookie(ctx.Resp, OauthPKCECookieName, ascii, hs.Cfg.OAuthCookieMaxAge, hs.CookieOptionsFromCfg)
|
||||
cookies.WriteCookie(reqCtx.Resp, OauthPKCECookieName, ascii, hs.Cfg.OAuthCookieMaxAge, hs.CookieOptionsFromCfg)
|
||||
|
||||
opts = append(opts,
|
||||
oauth2.SetAuthURLParam("code_challenge", pkce),
|
||||
@@ -151,8 +151,8 @@ func (hs *HTTPServer) OAuthLogin(ctx *contextmodel.ReqContext) {
|
||||
|
||||
state, err := GenStateString()
|
||||
if err != nil {
|
||||
ctx.Logger.Error("Generating state string failed", "err", err)
|
||||
hs.handleOAuthLoginError(ctx, loginInfo, LoginError{
|
||||
reqCtx.Logger.Error("Generating state string failed", "err", err)
|
||||
hs.handleOAuthLoginError(reqCtx, loginInfo, LoginError{
|
||||
HttpStatus: http.StatusInternalServerError,
|
||||
PublicMessage: "An internal error occurred",
|
||||
})
|
||||
@@ -160,32 +160,32 @@ func (hs *HTTPServer) OAuthLogin(ctx *contextmodel.ReqContext) {
|
||||
}
|
||||
|
||||
hashedState := hs.hashStatecode(state, provider.ClientSecret)
|
||||
cookies.WriteCookie(ctx.Resp, OauthStateCookieName, hashedState, hs.Cfg.OAuthCookieMaxAge, hs.CookieOptionsFromCfg)
|
||||
cookies.WriteCookie(reqCtx.Resp, OauthStateCookieName, hashedState, hs.Cfg.OAuthCookieMaxAge, hs.CookieOptionsFromCfg)
|
||||
if provider.HostedDomain != "" {
|
||||
opts = append(opts, oauth2.SetAuthURLParam("hd", provider.HostedDomain))
|
||||
}
|
||||
|
||||
ctx.Redirect(connect.AuthCodeURL(state, opts...))
|
||||
reqCtx.Redirect(connect.AuthCodeURL(state, opts...))
|
||||
return
|
||||
}
|
||||
|
||||
cookieState := ctx.GetCookie(OauthStateCookieName)
|
||||
cookieState := reqCtx.GetCookie(OauthStateCookieName)
|
||||
|
||||
// delete cookie
|
||||
cookies.DeleteCookie(ctx.Resp, OauthStateCookieName, hs.CookieOptionsFromCfg)
|
||||
cookies.DeleteCookie(reqCtx.Resp, OauthStateCookieName, hs.CookieOptionsFromCfg)
|
||||
|
||||
if cookieState == "" {
|
||||
hs.handleOAuthLoginError(ctx, loginInfo, LoginError{
|
||||
hs.handleOAuthLoginError(reqCtx, loginInfo, LoginError{
|
||||
HttpStatus: http.StatusInternalServerError,
|
||||
PublicMessage: "login.OAuthLogin(missing saved state)",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
queryState := hs.hashStatecode(ctx.Query("state"), provider.ClientSecret)
|
||||
queryState := hs.hashStatecode(reqCtx.Query("state"), provider.ClientSecret)
|
||||
oauthLogger.Info("state check", "queryState", queryState, "cookieState", cookieState)
|
||||
if cookieState != queryState {
|
||||
hs.handleOAuthLoginError(ctx, loginInfo, LoginError{
|
||||
hs.handleOAuthLoginError(reqCtx, loginInfo, LoginError{
|
||||
HttpStatus: http.StatusInternalServerError,
|
||||
PublicMessage: "login.OAuthLogin(state mismatch)",
|
||||
})
|
||||
@@ -194,19 +194,20 @@ func (hs *HTTPServer) OAuthLogin(ctx *contextmodel.ReqContext) {
|
||||
|
||||
oauthClient, err := hs.SocialService.GetOAuthHttpClient(name)
|
||||
if err != nil {
|
||||
ctx.Logger.Error("Failed to create OAuth http client", "error", err)
|
||||
hs.handleOAuthLoginError(ctx, loginInfo, LoginError{
|
||||
reqCtx.Logger.Error("Failed to create OAuth http client", "error", err)
|
||||
hs.handleOAuthLoginError(reqCtx, loginInfo, LoginError{
|
||||
HttpStatus: http.StatusInternalServerError,
|
||||
PublicMessage: "login.OAuthLogin(" + err.Error() + ")",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
oauthCtx := context.WithValue(context.Background(), oauth2.HTTPClient, oauthClient)
|
||||
ctx := reqCtx.Req.Context()
|
||||
oauthCtx := context.WithValue(ctx, oauth2.HTTPClient, oauthClient)
|
||||
opts := []oauth2.AuthCodeOption{}
|
||||
|
||||
codeVerifier := ctx.GetCookie(OauthPKCECookieName)
|
||||
cookies.DeleteCookie(ctx.Resp, OauthPKCECookieName, hs.CookieOptionsFromCfg)
|
||||
codeVerifier := reqCtx.GetCookie(OauthPKCECookieName)
|
||||
cookies.DeleteCookie(reqCtx.Resp, OauthPKCECookieName, hs.CookieOptionsFromCfg)
|
||||
if codeVerifier != "" {
|
||||
opts = append(opts,
|
||||
oauth2.SetAuthURLParam("code_verifier", codeVerifier),
|
||||
@@ -216,7 +217,7 @@ func (hs *HTTPServer) OAuthLogin(ctx *contextmodel.ReqContext) {
|
||||
// get token from provider
|
||||
token, err := connect.Exchange(oauthCtx, code, opts...)
|
||||
if err != nil {
|
||||
hs.handleOAuthLoginError(ctx, loginInfo, LoginError{
|
||||
hs.handleOAuthLoginError(reqCtx, loginInfo, LoginError{
|
||||
HttpStatus: http.StatusInternalServerError,
|
||||
PublicMessage: "login.OAuthLogin(NewTransportWithCode)",
|
||||
Err: err,
|
||||
@@ -245,13 +246,13 @@ func (hs *HTTPServer) OAuthLogin(ctx *contextmodel.ReqContext) {
|
||||
client := connect.Client(oauthCtx, token)
|
||||
|
||||
// get user info
|
||||
userInfo, err := connect.UserInfo(client, token)
|
||||
userInfo, err := connect.UserInfo(ctx, client, token)
|
||||
if err != nil {
|
||||
var sErr *social.Error
|
||||
if errors.As(err, &sErr) {
|
||||
hs.handleOAuthLoginErrorWithRedirect(ctx, loginInfo, sErr)
|
||||
hs.handleOAuthLoginErrorWithRedirect(reqCtx, loginInfo, sErr)
|
||||
} else {
|
||||
hs.handleOAuthLoginError(ctx, loginInfo, LoginError{
|
||||
hs.handleOAuthLoginError(reqCtx, loginInfo, LoginError{
|
||||
HttpStatus: http.StatusInternalServerError,
|
||||
PublicMessage: fmt.Sprintf("login.OAuthLogin(get info from %s)", name),
|
||||
Err: err,
|
||||
@@ -264,34 +265,34 @@ func (hs *HTTPServer) OAuthLogin(ctx *contextmodel.ReqContext) {
|
||||
|
||||
// validate that we got at least an email address
|
||||
if userInfo.Email == "" {
|
||||
hs.handleOAuthLoginErrorWithRedirect(ctx, loginInfo, login.ErrNoEmail)
|
||||
hs.handleOAuthLoginErrorWithRedirect(reqCtx, loginInfo, login.ErrNoEmail)
|
||||
return
|
||||
}
|
||||
|
||||
// validate that the email is allowed to login to grafana
|
||||
if !connect.IsEmailAllowed(userInfo.Email) {
|
||||
hs.handleOAuthLoginErrorWithRedirect(ctx, loginInfo, login.ErrEmailNotAllowed)
|
||||
hs.handleOAuthLoginErrorWithRedirect(reqCtx, loginInfo, login.ErrEmailNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
loginInfo.ExternalUser = *hs.buildExternalUserInfo(token, userInfo, name)
|
||||
loginInfo.User, err = hs.SyncUser(ctx, &loginInfo.ExternalUser, connect)
|
||||
loginInfo.User, err = hs.SyncUser(reqCtx, &loginInfo.ExternalUser, connect)
|
||||
if err != nil {
|
||||
hs.handleOAuthLoginErrorWithRedirect(ctx, loginInfo, err)
|
||||
hs.handleOAuthLoginErrorWithRedirect(reqCtx, loginInfo, err)
|
||||
return
|
||||
}
|
||||
|
||||
// login
|
||||
if err := hs.loginUserWithUser(loginInfo.User, ctx); err != nil {
|
||||
hs.handleOAuthLoginErrorWithRedirect(ctx, loginInfo, err)
|
||||
if err := hs.loginUserWithUser(loginInfo.User, reqCtx); err != nil {
|
||||
hs.handleOAuthLoginErrorWithRedirect(reqCtx, loginInfo, err)
|
||||
return
|
||||
}
|
||||
|
||||
loginInfo.HTTPStatus = http.StatusOK
|
||||
hs.HooksService.RunLoginHook(&loginInfo, ctx)
|
||||
hs.HooksService.RunLoginHook(&loginInfo, reqCtx)
|
||||
metrics.MApiLoginOAuth.Inc()
|
||||
|
||||
ctx.Redirect(hs.GetRedirectURL(ctx))
|
||||
reqCtx.Redirect(hs.GetRedirectURL(reqCtx))
|
||||
}
|
||||
|
||||
// buildExternalUserInfo returns a ExternalUserInfo struct from OAuth user profile
|
||||
|
||||
Reference in New Issue
Block a user