From df7923f35b088f108824f4f00b806bdfaf848a8f Mon Sep 17 00:00:00 2001 From: Anbraten <6918444+anbraten@users.noreply.github.com> Date: Wed, 19 Jun 2024 06:23:40 +0200 Subject: [PATCH 01/15] use oauth state --- cmd/server/server.go | 7 ++ cmd/server/setup.go | 34 ++++++ server/api/login.go | 104 ++++++++++-------- server/config.go | 3 +- server/forge/bitbucket/bitbucket.go | 2 +- .../bitbucketdatacenter.go | 4 +- server/forge/forgejo/forgejo.go | 2 +- server/forge/gitea/gitea.go | 2 +- server/forge/github/github.go | 2 +- server/forge/gitlab/gitlab.go | 2 +- server/forge/types/oauth.go | 1 + shared/token/token.go | 11 +- 12 files changed, 115 insertions(+), 59 deletions(-) diff --git a/cmd/server/server.go b/cmd/server/server.go index 76e73679b0..47946848b7 100644 --- a/cmd/server/server.go +++ b/cmd/server/server.go @@ -265,6 +265,13 @@ func run(c *cli.Context) error { } func setupEvilGlobals(c *cli.Context, s store.Store) error { + // secrets + var err error + server.Config.JWTSecret, err = setupJWTSecret(s) + if err != nil { + return fmt.Errorf("could not setup jwt secret: %w", err) + } + // services server.Config.Services.Queue = setupQueue(c, s) server.Config.Services.Logs = logging.New() diff --git a/cmd/server/setup.go b/cmd/server/setup.go index b22e8e01ac..02b8a37079 100644 --- a/cmd/server/setup.go +++ b/cmd/server/setup.go @@ -17,6 +17,9 @@ package main import ( "context" + "crypto/rand" + "encoding/base64" + "errors" "fmt" "os" "time" @@ -34,6 +37,7 @@ import ( "go.woodpecker-ci.org/woodpecker/v2/server/services/log/file" "go.woodpecker-ci.org/woodpecker/v2/server/store" "go.woodpecker-ci.org/woodpecker/v2/server/store/datastore" + "go.woodpecker-ci.org/woodpecker/v2/server/store/types" ) func setupStore(c *cli.Context) (store.Store, error) { @@ -165,3 +169,33 @@ func setupLogStore(c *cli.Context, s store.Store) (logService.Service, error) { return s, nil } } + +const jwtSecretID = "jwt-secret" + +func randToken() (string, error) { + b := make([]byte, 32) + if _, err := rand.Read(b); err != nil { + return "", err + } + return base64.StdEncoding.EncodeToString(b), nil +} + +func setupJWTSecret(_store store.Store) (string, error) { + jwtSecret, err := _store.ServerConfigGet(jwtSecretID) + if errors.Is(err, types.RecordNotExist) { + jwtSecret, err := randToken() + if err != nil { + return "", err + } + err = _store.ServerConfigSet(jwtSecretID, jwtSecret) + if err != nil { + return "", err + } + log.Debug().Msg("created jwt secret") + return jwtSecret, nil + } else if err != nil { + return "", err + } + + return jwtSecret, nil +} diff --git a/server/api/login.go b/server/api/login.go index c03de5db2a..c88cf348cb 100644 --- a/server/api/login.go +++ b/server/api/login.go @@ -17,6 +17,7 @@ package api import ( "encoding/base32" "errors" + "fmt" "net/http" "strconv" "time" @@ -44,18 +45,29 @@ func HandleLogin(c *gin.Context) { func HandleAuth(c *gin.Context) { _store := store.FromContext(c) + forgeID := int64(1) // TODO: replace with forge id when multiple forges are supported _forge, err := server.Config.Services.Manager.ForgeMain() if err != nil { _ = c.AbortWithError(http.StatusInternalServerError, err) return } - forgeID := int64(1) // TODO: replace with forge id when multiple forges are supported // when dealing with redirects, we may need to adjust the content type. I // cannot, however, remember why, so need to revisit this line. c.Writer.Header().Del("Content-Type") - tmpUser, redirectURL, err := _forge.Login(c, &forge_types.OAuthRequest{ + exp := time.Now().Add(time.Minute * 15).Unix() + stateToken := token.New(token.OAuthStateToken) + stateToken.Set("forge-id", fmt.Sprintf("%d", forgeID)) + state, err := stateToken.SignExpires(server.Config.JWTSecret, exp) + if err != nil { + log.Error().Err(err).Msg("cannot create state token") + c.Redirect(http.StatusSeeOther, server.Config.Server.RootPath+"/login?error=internal_error") + return + } + + userFromForge, redirectURL, err := _forge.Login(c, &forge_types.OAuthRequest{ + State: state, Error: c.Request.FormValue("error"), ErrorURI: c.Request.FormValue("error_uri"), ErrorDescription: c.Request.FormValue("error_description"), @@ -67,13 +79,13 @@ func HandleAuth(c *gin.Context) { return } // The user is not authorized yet -> redirect - if tmpUser == nil { + if userFromForge == nil { http.Redirect(c.Writer, c.Request, redirectURL, http.StatusSeeOther) return } // get the user from the database - u, err := _store.GetUserRemoteID(tmpUser.ForgeRemoteID, tmpUser.Login) + user, err := _store.GetUserRemoteID(userFromForge.ForgeRemoteID, userFromForge.Login) if err != nil && !errors.Is(err, types.RecordNotExist) { _ = c.AbortWithError(http.StatusInternalServerError, err) return @@ -81,8 +93,8 @@ func HandleAuth(c *gin.Context) { if errors.Is(err, types.RecordNotExist) { // if self-registration is disabled we should return a not authorized error - if !server.Config.Permissions.Open && !server.Config.Permissions.Admins.IsAdmin(tmpUser) { - log.Error().Msgf("cannot register %s. registration closed", tmpUser.Login) + if !server.Config.Permissions.Open && !server.Config.Permissions.Admins.IsAdmin(userFromForge) { + log.Error().Msgf("cannot register %s. registration closed", userFromForge.Login) c.Redirect(http.StatusSeeOther, server.Config.Server.RootPath+"/login?error=access_denied") return } @@ -90,22 +102,22 @@ func HandleAuth(c *gin.Context) { // if self-registration is enabled for allowed organizations we need to // check the user's organization membership. if server.Config.Permissions.Orgs.IsConfigured { - teams, terr := _forge.Teams(c, tmpUser) + teams, terr := _forge.Teams(c, userFromForge) if terr != nil || !server.Config.Permissions.Orgs.IsMember(teams) { - log.Error().Err(terr).Msgf("cannot verify team membership for %s.", u.Login) + log.Error().Err(terr).Msgf("cannot verify team membership for %s.", user.Login) c.Redirect(http.StatusSeeOther, server.Config.Server.RootPath+"/login?error=access_denied") return } } // create the user account - u = &model.User{ - Login: tmpUser.Login, - ForgeRemoteID: tmpUser.ForgeRemoteID, - Token: tmpUser.Token, - Secret: tmpUser.Secret, - Email: tmpUser.Email, - Avatar: tmpUser.Avatar, + user = &model.User{ + Login: userFromForge.Login, + ForgeRemoteID: userFromForge.ForgeRemoteID, + Token: userFromForge.Token, + Secret: userFromForge.Secret, + Email: userFromForge.Email, + Avatar: userFromForge.Avatar, ForgeID: forgeID, Hash: base32.StdEncoding.EncodeToString( securecookie.GenerateRandomKey(32), @@ -113,17 +125,17 @@ func HandleAuth(c *gin.Context) { } // insert the user into the database - if err := _store.CreateUser(u); err != nil { - log.Error().Err(err).Msgf("cannot insert %s", u.Login) + if err := _store.CreateUser(user); err != nil { + log.Error().Err(err).Msgf("cannot insert %s", user.Login) c.Redirect(http.StatusSeeOther, server.Config.Server.RootPath+"/login?error=internal_error") return } // if another user already have activated repos on behave of that user, // the user was stored as org. now we adopt it to the user. - if org, err := _store.OrgFindByName(u.Login); err == nil && org != nil { + if org, err := _store.OrgFindByName(user.Login); err == nil && org != nil { org.IsUser = true - u.OrgID = org.ID + user.OrgID = org.ID if err := _store.OrgUpdate(org); err != nil { log.Error().Err(err).Msgf("on user creation, could not mark org as user") } @@ -133,76 +145,76 @@ func HandleAuth(c *gin.Context) { return } org = &model.Org{ - Name: u.Login, + Name: user.Login, IsUser: true, Private: false, - ForgeID: u.ForgeID, + ForgeID: user.ForgeID, } if err := _store.OrgCreate(org); err != nil { log.Error().Err(err).Msgf("on user creation, could not create org for user") } - u.OrgID = org.ID + user.OrgID = org.ID } } // update org name - if u.Login != tmpUser.Login { - org, err := _store.OrgGet(u.OrgID) + if user.Login != userFromForge.Login { + org, err := _store.OrgGet(user.OrgID) if err != nil { - log.Error().Err(err).Msgf("cannot get org %s", u.Login) + log.Error().Err(err).Msgf("cannot get org %s", user.Login) c.Redirect(http.StatusSeeOther, server.Config.Server.RootPath+"/login?error=internal_error") return } - org.Name = u.Login + org.Name = user.Login if err := _store.OrgUpdate(org); err != nil { log.Error().Err(err).Msgf("on user creation, could not mark org as user") } } // update the user meta data and authorization data. - u.Token = tmpUser.Token - u.Secret = tmpUser.Secret - u.Email = tmpUser.Email - u.Avatar = tmpUser.Avatar - u.ForgeRemoteID = tmpUser.ForgeRemoteID - u.Login = tmpUser.Login - u.Admin = u.Admin || server.Config.Permissions.Admins.IsAdmin(tmpUser) + user.Token = userFromForge.Token + user.Secret = userFromForge.Secret + user.Email = userFromForge.Email + user.Avatar = userFromForge.Avatar + user.ForgeRemoteID = userFromForge.ForgeRemoteID + user.Login = userFromForge.Login + user.Admin = user.Admin || server.Config.Permissions.Admins.IsAdmin(userFromForge) // if self-registration is enabled for allowed organizations we need to // check the user's organization membership. if server.Config.Permissions.Orgs.IsConfigured { - teams, terr := _forge.Teams(c, u) + teams, terr := _forge.Teams(c, user) if terr != nil || !server.Config.Permissions.Orgs.IsMember(teams) { - log.Error().Err(terr).Msgf("cannot verify team membership for %s", u.Login) + log.Error().Err(terr).Msgf("cannot verify team membership for %s", user.Login) c.Redirect(http.StatusSeeOther, server.Config.Server.RootPath+"/login?error=access_denied") return } } - if err := _store.UpdateUser(u); err != nil { - log.Error().Err(err).Msgf("cannot update %s", u.Login) + if err := _store.UpdateUser(user); err != nil { + log.Error().Err(err).Msgf("cannot update %s", user.Login) c.Redirect(http.StatusSeeOther, server.Config.Server.RootPath+"/login?error=internal_error") return } - exp := time.Now().Add(server.Config.Server.SessionExpires).Unix() + exp = time.Now().Add(server.Config.Server.SessionExpires).Unix() _token := token.New(token.SessToken) - _token.Set("user-id", strconv.FormatInt(u.ID, 10)) - tokenString, err := _token.SignExpires(u.Hash, exp) + _token.Set("user-id", strconv.FormatInt(user.ID, 10)) + tokenString, err := _token.SignExpires(user.Hash, exp) if err != nil { - log.Error().Msgf("cannot create token for %s", u.Login) + log.Error().Msgf("cannot create token for %s", user.Login) c.Redirect(http.StatusSeeOther, server.Config.Server.RootPath+"/login?error=internal_error") return } - repos, _ := _forge.Repos(c, u) + repos, _ := _forge.Repos(c, user) for _, forgeRepo := range repos { dbRepo, err := _store.GetRepoForgeID(forgeRepo.ForgeRemoteID) if err != nil && errors.Is(err, types.RecordNotExist) { continue } if err != nil { - log.Error().Err(err).Msgf("cannot list repos for %s", u.Login) + log.Error().Err(err).Msgf("cannot list repos for %s", user.Login) c.Redirect(http.StatusSeeOther, "/login?error=internal_error") return } @@ -211,14 +223,14 @@ func HandleAuth(c *gin.Context) { continue } - log.Debug().Msgf("synced user permission for %s %s", u.Login, dbRepo.FullName) + log.Debug().Msgf("synced user permission for %s %s", user.Login, dbRepo.FullName) perm := forgeRepo.Perm perm.Repo = dbRepo perm.RepoID = dbRepo.ID - perm.UserID = u.ID + perm.UserID = user.ID perm.Synced = time.Now().Unix() if err := _store.PermUpsert(perm); err != nil { - log.Error().Err(err).Msgf("cannot update permissions for %s", u.Login) + log.Error().Err(err).Msgf("cannot update permissions for %s", user.Login) c.Redirect(http.StatusSeeOther, "/login?error=internal_error") return } diff --git a/server/config.go b/server/config.go index 0ebcce5fc3..ffb0779cc1 100644 --- a/server/config.go +++ b/server/config.go @@ -29,7 +29,8 @@ import ( ) var Config = struct { - Services struct { + JWTSecret string + Services struct { Pubsub *pubsub.Publisher Queue queue.Queue Logs logging.Log diff --git a/server/forge/bitbucket/bitbucket.go b/server/forge/bitbucket/bitbucket.go index c870477887..60f2bed4bd 100644 --- a/server/forge/bitbucket/bitbucket.go +++ b/server/forge/bitbucket/bitbucket.go @@ -81,7 +81,7 @@ func (c *config) URL() string { // Bitbucket account details are returned when the user is successfully authenticated. func (c *config) Login(ctx context.Context, req *forge_types.OAuthRequest) (*model.User, string, error) { config := c.newOAuth2Config() - redirectURL := config.AuthCodeURL("woodpecker") + redirectURL := config.AuthCodeURL(req.State) // get the OAuth errors if req.Error != "" { diff --git a/server/forge/bitbucketdatacenter/bitbucketdatacenter.go b/server/forge/bitbucketdatacenter/bitbucketdatacenter.go index 573ee52e38..9edaf91503 100644 --- a/server/forge/bitbucketdatacenter/bitbucketdatacenter.go +++ b/server/forge/bitbucketdatacenter/bitbucketdatacenter.go @@ -96,8 +96,8 @@ func (c *client) URL() string { func (c *client) Login(ctx context.Context, req *forge_types.OAuthRequest) (*model.User, string, error) { config := c.newOAuth2Config() - // TODO: Add proper state and pkce (https://oauth.net/2/pkce/) ... - redirectURL := config.AuthCodeURL("woodpecker") + // TODO: Use proper pkce (https://oauth.net/2/pkce/) ... + redirectURL := config.AuthCodeURL(req.State) if req.Error != "" { return nil, redirectURL, &forge_types.AuthError{ diff --git a/server/forge/forgejo/forgejo.go b/server/forge/forgejo/forgejo.go index af7153fe3c..91f1b2c40a 100644 --- a/server/forge/forgejo/forgejo.go +++ b/server/forge/forgejo/forgejo.go @@ -111,7 +111,7 @@ func (c *Forgejo) oauth2Config(ctx context.Context) (*oauth2.Config, context.Con // Forgejo account details are returned when the user is successfully authenticated. func (c *Forgejo) Login(ctx context.Context, req *forge_types.OAuthRequest) (*model.User, string, error) { config, oauth2Ctx := c.oauth2Config(ctx) - redirectURL := config.AuthCodeURL("woodpecker") + redirectURL := config.AuthCodeURL(req.State) // check the OAuth errors if req.Error != "" { diff --git a/server/forge/gitea/gitea.go b/server/forge/gitea/gitea.go index e0d82c5062..09fd21b633 100644 --- a/server/forge/gitea/gitea.go +++ b/server/forge/gitea/gitea.go @@ -113,7 +113,7 @@ func (c *Gitea) oauth2Config(ctx context.Context) (*oauth2.Config, context.Conte // Gitea account details are returned when the user is successfully authenticated. func (c *Gitea) Login(ctx context.Context, req *forge_types.OAuthRequest) (*model.User, string, error) { config, oauth2Ctx := c.oauth2Config(ctx) - redirectURL := config.AuthCodeURL("woodpecker") + redirectURL := config.AuthCodeURL(req.State) // check the OAuth errors if req.Error != "" { diff --git a/server/forge/github/github.go b/server/forge/github/github.go index 0e62413cf4..3f4204ed2e 100644 --- a/server/forge/github/github.go +++ b/server/forge/github/github.go @@ -100,7 +100,7 @@ func (c *client) URL() string { // Login authenticates the session and returns the forge user details. func (c *client) Login(ctx context.Context, req *forge_types.OAuthRequest) (*model.User, string, error) { config := c.newConfig() - redirectURL := config.AuthCodeURL("woodpecker") + redirectURL := config.AuthCodeURL(req.State) // check the OAuth errors if req.Error != "" { diff --git a/server/forge/gitlab/gitlab.go b/server/forge/gitlab/gitlab.go index 27cd50cb7f..d092fd77a5 100644 --- a/server/forge/gitlab/gitlab.go +++ b/server/forge/gitlab/gitlab.go @@ -115,7 +115,7 @@ func (g *GitLab) oauth2Config(ctx context.Context) (*oauth2.Config, context.Cont // forge user details. func (g *GitLab) Login(ctx context.Context, req *forge_types.OAuthRequest) (*model.User, string, error) { config, oauth2Ctx := g.oauth2Config(ctx) - redirectURL := config.AuthCodeURL("woodpecker") + redirectURL := config.AuthCodeURL(req.State) // check the OAuth errors if req.Error != "" { diff --git a/server/forge/types/oauth.go b/server/forge/types/oauth.go index fba870569c..c2ea8f4c51 100644 --- a/server/forge/types/oauth.go +++ b/server/forge/types/oauth.go @@ -19,4 +19,5 @@ type OAuthRequest struct { ErrorURI string ErrorDescription string Code string + State string } diff --git a/shared/token/token.go b/shared/token/token.go index 545107f591..8958b67dfe 100644 --- a/shared/token/token.go +++ b/shared/token/token.go @@ -25,11 +25,12 @@ import ( type SecretFunc func(*Token) (string, error) const ( - UserToken = "user" - SessToken = "sess" - HookToken = "hook" - CsrfToken = "csrf" - AgentToken = "agent" + UserToken = "user" + SessToken = "sess" + HookToken = "hook" + CsrfToken = "csrf" + AgentToken = "agent" + OAuthStateToken = "oauth-state" ) // SignerAlgo id default algorithm used to sign JWT tokens. From 8f1ccfaa05fdfc4cb38ce013aaee05cc8c353878 Mon Sep 17 00:00:00 2001 From: Anbraten <6918444+anbraten@users.noreply.github.com> Date: Thu, 20 Jun 2024 15:19:16 +0200 Subject: [PATCH 02/15] simplify login flow --- server/api/login.go | 82 ++++++++++++++++------- server/router/router.go | 3 +- shared/token/token.go | 12 ++-- web/src/compositions/useAuthentication.ts | 2 +- web/src/router.ts | 2 +- 5 files changed, 68 insertions(+), 33 deletions(-) diff --git a/server/api/login.go b/server/api/login.go index c88cf348cb..fc5f73ed58 100644 --- a/server/api/login.go +++ b/server/api/login.go @@ -35,14 +35,6 @@ import ( "go.woodpecker-ci.org/woodpecker/v2/shared/token" ) -func HandleLogin(c *gin.Context) { - if err := c.Request.FormValue("error"); err != "" { - c.Redirect(http.StatusSeeOther, server.Config.Server.RootPath+"/login/error?code="+err) - } else { - c.Redirect(http.StatusSeeOther, server.Config.Server.RootPath+"/authorize") - } -} - func HandleAuth(c *gin.Context) { _store := store.FromContext(c) forgeID := int64(1) // TODO: replace with forge id when multiple forges are supported @@ -52,21 +44,69 @@ func HandleAuth(c *gin.Context) { return } + // oauth flow + // 1. send user to forge: https://forge/oauth/authorize?client_id=...&redirect_uri=...&state=... + // 2. user comes back from forge + // 2a) with ?error=...: redirect to login?error=... (UI route. It will handle error and show message) + // 2b) with ?code=...: check state and continue to exchange code for token + // 3. exchange code for token: POST https://forge/oauth/token?client_id=...&client_secret=...&code + // when dealing with redirects, we may need to adjust the content type. I // cannot, however, remember why, so need to revisit this line. c.Writer.Header().Del("Content-Type") - exp := time.Now().Add(time.Minute * 15).Unix() - stateToken := token.New(token.OAuthStateToken) - stateToken.Set("forge-id", fmt.Sprintf("%d", forgeID)) - state, err := stateToken.SignExpires(server.Config.JWTSecret, exp) + // check the OAuth errors + if c.Request.FormValue("error") != "" { + redirectURL := _forge.GetRedirectURL(c, forgeID) + + http.Redirect(c.Writer, c.Request, redirectURL, http.StatusSeeOther) + return + } + + // check the OAuth code + code := c.Request.FormValue("code") + if len(code) == 0 { + exp := time.Now().Add(time.Minute * 15).Unix() + stateToken := token.New(token.OAuthStateToken) + stateToken.Set("forge-id", fmt.Sprintf("%d", forgeID)) + state, err := stateToken.SignExpires(server.Config.JWTSecret, exp) + if err != nil { + log.Error().Err(err).Msg("cannot create state token") + c.Redirect(http.StatusSeeOther, server.Config.Server.RootPath+"/login?error=internal_error") + return + } + + redirectURL := _forge.GetRedirectURL(c, &forge_types.OAuthRequest{ + State: state, + Code: c.Request.FormValue("code"), + }) + + http.Redirect(c.Writer, c.Request, redirectURL, http.StatusSeeOther) + return + } + + // check the OAuth state + state := c.Request.FormValue("state") + if len(state) == 0 { + log.Error().Msg("missing state token") + c.Redirect(http.StatusSeeOther, server.Config.Server.RootPath+"/login?error=oauth_error") + return + } + + // check the OAuth state token + _token, err := token.Parse(state, func(t *token.Token) (string, error) { + if t.Kind != token.OAuthStateToken { + return "", fmt.Errorf("invalid token type") + } + return server.Config.JWTSecret, nil + }) if err != nil { - log.Error().Err(err).Msg("cannot create state token") - c.Redirect(http.StatusSeeOther, server.Config.Server.RootPath+"/login?error=internal_error") + log.Error().Err(err).Msg("cannot parse state token") + c.Redirect(http.StatusSeeOther, server.Config.Server.RootPath+"/login?error=oauth_error") return } - userFromForge, redirectURL, err := _forge.Login(c, &forge_types.OAuthRequest{ + userFromForge, err := _forge.Login(c, &forge_types.OAuthRequest{ State: state, Error: c.Request.FormValue("error"), ErrorURI: c.Request.FormValue("error_uri"), @@ -78,11 +118,6 @@ func HandleAuth(c *gin.Context) { c.Redirect(http.StatusSeeOther, server.Config.Server.RootPath+"/login?error=oauth_error") return } - // The user is not authorized yet -> redirect - if userFromForge == nil { - http.Redirect(c.Writer, c.Request, redirectURL, http.StatusSeeOther) - return - } // get the user from the database user, err := _store.GetUserRemoteID(userFromForge.ForgeRemoteID, userFromForge.Login) @@ -197,8 +232,8 @@ func HandleAuth(c *gin.Context) { return } - exp = time.Now().Add(server.Config.Server.SessionExpires).Unix() - _token := token.New(token.SessToken) + exp := time.Now().Add(server.Config.Server.SessionExpires).Unix() + _token = token.New(token.SessToken) _token.Set("user-id", strconv.FormatInt(user.ID, 10)) tokenString, err := _token.SignExpires(user.Hash, exp) if err != nil { @@ -247,7 +282,8 @@ func GetLogout(c *gin.Context) { c.Redirect(http.StatusSeeOther, server.Config.Server.RootPath+"/") } -func GetLoginToken(c *gin.Context) { +// TODO: remove in 3.0 +func DeprecatedGetLoginToken(c *gin.Context) { _store := store.FromContext(c) _forge, err := server.Config.Services.Manager.ForgeMain() // TODO: get selected forge from auth request diff --git a/server/router/router.go b/server/router/router.go index 317f0975d2..bfd2397872 100644 --- a/server/router/router.go +++ b/server/router/router.go @@ -58,12 +58,11 @@ func Load(noRouteHandler http.HandlerFunc, middleware ...gin.HandlerFunc) http.H base.GET("/web-config.js", web.Config) base.GET("/logout", api.GetLogout) - base.GET("/login", api.HandleLogin) auth := base.Group("/authorize") { auth.GET("", api.HandleAuth) auth.POST("", api.HandleAuth) - auth.POST("/token", api.GetLoginToken) + auth.POST("/token", api.DeprecatedGetLoginToken) // Not used anymore } base.GET("/metrics", metrics.PromHandler()) diff --git a/shared/token/token.go b/shared/token/token.go index 8958b67dfe..7e1e595855 100644 --- a/shared/token/token.go +++ b/shared/token/token.go @@ -41,7 +41,7 @@ type Token struct { claims jwt.MapClaims } -func parse(raw string, fn SecretFunc) (*Token, error) { +func Parse(raw string, fn SecretFunc) (*Token, error) { token := &Token{ claims: jwt.MapClaims{}, } @@ -64,19 +64,19 @@ func ParseRequest(r *http.Request, fn SecretFunc) (*Token, error) { if _, err := fmt.Sscanf(token, "Bearer %s", &bearer); err != nil { return nil, err } - return parse(bearer, fn) + return Parse(bearer, fn) } token = r.Header.Get("X-Gitlab-Token") if len(token) != 0 { - return parse(token, fn) + return Parse(token, fn) } // then we attempt to get the token from the // access_token url query parameter token = r.FormValue("access_token") if len(token) != 0 { - return parse(token, fn) + return Parse(token, fn) } // and finally we attempt to get the token from @@ -85,7 +85,7 @@ func ParseRequest(r *http.Request, fn SecretFunc) (*Token, error) { if err != nil { return nil, err } - return parse(cookie.Value, fn) + return Parse(cookie.Value, fn) } func CheckCsrf(r *http.Request, fn SecretFunc) error { @@ -98,7 +98,7 @@ func CheckCsrf(r *http.Request, fn SecretFunc) error { // parse the raw CSRF token value and validate raw := r.Header.Get("X-CSRF-TOKEN") - _, err := parse(raw, fn) + _, err := Parse(raw, fn) return err } diff --git a/web/src/compositions/useAuthentication.ts b/web/src/compositions/useAuthentication.ts index bd7b6d0b11..d8bcb21fda 100644 --- a/web/src/compositions/useAuthentication.ts +++ b/web/src/compositions/useAuthentication.ts @@ -12,6 +12,6 @@ export default () => const config = useUserConfig(); config.setUserConfig('redirectUrl', url); } - window.location.href = `${useConfig().rootPath}/login`; + window.location.href = `${useConfig().rootPath}/authorize`; }, }) as const; diff --git a/web/src/router.ts b/web/src/router.ts index 21f97afac1..26918ee838 100644 --- a/web/src/router.ts +++ b/web/src/router.ts @@ -160,7 +160,7 @@ const routes: RouteRecordRaw[] = [ props: true, }, { - path: `${rootPath}/do-login`, + path: `${rootPath}/login`, name: 'login', component: (): Component => import('~/views/Login.vue'), meta: { blank: true }, From 9b2d92e76be934c6ac1612e7f19bf79598c36952 Mon Sep 17 00:00:00 2001 From: Anbraten <6918444+anbraten@users.noreply.github.com> Date: Thu, 20 Jun 2024 15:40:05 +0200 Subject: [PATCH 03/15] fix ui --- web/src/assets/locales/en.json | 3 ++- web/src/components/admin/settings/AdminInfoTab.vue | 1 + web/src/components/atomic/Error.vue | 2 +- web/src/components/atomic/Warning.vue | 2 +- web/src/views/Login.vue | 8 ++++---- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/web/src/assets/locales/en.json b/web/src/assets/locales/en.json index a6586152e7..27bdd0082b 100644 --- a/web/src/assets/locales/en.json +++ b/web/src/assets/locales/en.json @@ -451,5 +451,6 @@ "cli_login_failed": "Login to CLI failed", "cli_login_denied": "Login to CLI denied", "return_to_cli": "You can now close this tab and return to the CLI.", - "settings": "Settings" + "settings": "Settings", + "unknown_auth_error": "An unknown authentication error occurred: {error}" } diff --git a/web/src/components/admin/settings/AdminInfoTab.vue b/web/src/components/admin/settings/AdminInfoTab.vue index 8f91823616..da965f7eea 100644 --- a/web/src/components/admin/settings/AdminInfoTab.vue +++ b/web/src/components/admin/settings/AdminInfoTab.vue @@ -29,6 +29,7 @@ diff --git a/web/src/components/atomic/Warning.vue b/web/src/components/atomic/Warning.vue index 418d8b525e..bf4cb74456 100644 --- a/web/src/components/atomic/Warning.vue +++ b/web/src/components/atomic/Warning.vue @@ -12,6 +12,6 @@ diff --git a/web/src/views/Login.vue b/web/src/views/Login.vue index d6fcec0b98..e906d1ee2b 100644 --- a/web/src/views/Login.vue +++ b/web/src/views/Login.vue @@ -1,6 +1,5 @@