mirror of
https://github.com/go-gitea/gitea
synced 2025-08-01 15:18:37 +00:00
Refactor OpenIDConnect to support SSH/FullName sync (#34978)
* Fix #26585 * Fix #28327 * Fix #34932
This commit is contained in:
@@ -14,7 +14,6 @@ import (
|
||||
"code.gitea.io/gitea/modules/templates"
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
"code.gitea.io/gitea/services/externalaccount"
|
||||
"code.gitea.io/gitea/services/forms"
|
||||
)
|
||||
|
||||
@@ -75,7 +74,7 @@ func TwoFactorPost(ctx *context.Context) {
|
||||
}
|
||||
|
||||
if ctx.Session.Get("linkAccount") != nil {
|
||||
err = externalaccount.LinkAccountFromStore(ctx, ctx.Session, u)
|
||||
err = linkAccountFromContext(ctx, u)
|
||||
if err != nil {
|
||||
ctx.ServerError("UserSignIn", err)
|
||||
return
|
||||
|
@@ -329,6 +329,7 @@ func handleSignInFull(ctx *context.Context, u *user_model.User, remember, obeyRe
|
||||
"twofaUid",
|
||||
"twofaRemember",
|
||||
"linkAccount",
|
||||
"linkAccountData",
|
||||
}, map[string]any{
|
||||
session.KeyUID: u.ID,
|
||||
session.KeyUname: u.Name,
|
||||
@@ -519,7 +520,7 @@ func SignUpPost(ctx *context.Context) {
|
||||
Passwd: form.Password,
|
||||
}
|
||||
|
||||
if !createAndHandleCreatedUser(ctx, tplSignUp, form, u, nil, nil, false) {
|
||||
if !createAndHandleCreatedUser(ctx, tplSignUp, form, u, nil, nil) {
|
||||
// error already handled
|
||||
return
|
||||
}
|
||||
@@ -530,22 +531,22 @@ func SignUpPost(ctx *context.Context) {
|
||||
|
||||
// createAndHandleCreatedUser calls createUserInContext and
|
||||
// then handleUserCreated.
|
||||
func createAndHandleCreatedUser(ctx *context.Context, tpl templates.TplName, form any, u *user_model.User, overwrites *user_model.CreateUserOverwriteOptions, gothUser *goth.User, allowLink bool) bool {
|
||||
if !createUserInContext(ctx, tpl, form, u, overwrites, gothUser, allowLink) {
|
||||
func createAndHandleCreatedUser(ctx *context.Context, tpl templates.TplName, form any, u *user_model.User, overwrites *user_model.CreateUserOverwriteOptions, possibleLinkAccountData *LinkAccountData) bool {
|
||||
if !createUserInContext(ctx, tpl, form, u, overwrites, possibleLinkAccountData) {
|
||||
return false
|
||||
}
|
||||
return handleUserCreated(ctx, u, gothUser)
|
||||
return handleUserCreated(ctx, u, possibleLinkAccountData)
|
||||
}
|
||||
|
||||
// createUserInContext creates a user and handles errors within a given context.
|
||||
// Optionally a template can be specified.
|
||||
func createUserInContext(ctx *context.Context, tpl templates.TplName, form any, u *user_model.User, overwrites *user_model.CreateUserOverwriteOptions, gothUser *goth.User, allowLink bool) (ok bool) {
|
||||
// Optionally, a template can be specified.
|
||||
func createUserInContext(ctx *context.Context, tpl templates.TplName, form any, u *user_model.User, overwrites *user_model.CreateUserOverwriteOptions, possibleLinkAccountData *LinkAccountData) (ok bool) {
|
||||
meta := &user_model.Meta{
|
||||
InitialIP: ctx.RemoteAddr(),
|
||||
InitialUserAgent: ctx.Req.UserAgent(),
|
||||
}
|
||||
if err := user_model.CreateUser(ctx, u, meta, overwrites); err != nil {
|
||||
if allowLink && (user_model.IsErrUserAlreadyExist(err) || user_model.IsErrEmailAlreadyUsed(err)) {
|
||||
if possibleLinkAccountData != nil && (user_model.IsErrUserAlreadyExist(err) || user_model.IsErrEmailAlreadyUsed(err)) {
|
||||
switch setting.OAuth2Client.AccountLinking {
|
||||
case setting.OAuth2AccountLinkingAuto:
|
||||
var user *user_model.User
|
||||
@@ -561,15 +562,15 @@ func createUserInContext(ctx *context.Context, tpl templates.TplName, form any,
|
||||
}
|
||||
|
||||
// TODO: probably we should respect 'remember' user's choice...
|
||||
linkAccount(ctx, user, *gothUser, true)
|
||||
oauth2LinkAccount(ctx, user, possibleLinkAccountData, true)
|
||||
return false // user is already created here, all redirects are handled
|
||||
case setting.OAuth2AccountLinkingLogin:
|
||||
showLinkingLogin(ctx, *gothUser)
|
||||
showLinkingLogin(ctx, &possibleLinkAccountData.AuthSource, possibleLinkAccountData.GothUser)
|
||||
return false // user will be created only after linking login
|
||||
}
|
||||
}
|
||||
|
||||
// handle error without template
|
||||
// handle error without a template
|
||||
if len(tpl) == 0 {
|
||||
ctx.ServerError("CreateUser", err)
|
||||
return false
|
||||
@@ -610,7 +611,7 @@ func createUserInContext(ctx *context.Context, tpl templates.TplName, form any,
|
||||
// handleUserCreated does additional steps after a new user is created.
|
||||
// It auto-sets admin for the only user, updates the optional external user and
|
||||
// sends a confirmation email if required.
|
||||
func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth.User) (ok bool) {
|
||||
func handleUserCreated(ctx *context.Context, u *user_model.User, possibleLinkAccountData *LinkAccountData) (ok bool) {
|
||||
// Auto-set admin for the only user.
|
||||
hasUsers, err := user_model.HasUsers(ctx)
|
||||
if err != nil {
|
||||
@@ -631,8 +632,8 @@ func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth.
|
||||
}
|
||||
|
||||
// update external user information
|
||||
if gothUser != nil {
|
||||
if err := externalaccount.EnsureLinkExternalToUser(ctx, u, *gothUser); err != nil {
|
||||
if possibleLinkAccountData != nil {
|
||||
if err := externalaccount.EnsureLinkExternalToUser(ctx, possibleLinkAccountData.AuthSource.ID, u, possibleLinkAccountData.GothUser); err != nil {
|
||||
log.Error("EnsureLinkExternalToUser failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
@@ -5,7 +5,6 @@ package auth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -21,8 +20,6 @@ import (
|
||||
"code.gitea.io/gitea/services/context"
|
||||
"code.gitea.io/gitea/services/externalaccount"
|
||||
"code.gitea.io/gitea/services/forms"
|
||||
|
||||
"github.com/markbates/goth"
|
||||
)
|
||||
|
||||
var tplLinkAccount templates.TplName = "user/auth/link_account"
|
||||
@@ -52,28 +49,28 @@ func LinkAccount(ctx *context.Context) {
|
||||
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin"
|
||||
ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/link_account_signup"
|
||||
|
||||
gothUser, ok := ctx.Session.Get("linkAccountGothUser").(goth.User)
|
||||
linkAccountData := oauth2GetLinkAccountData(ctx)
|
||||
|
||||
// If you'd like to quickly debug the "link account" page layout, just uncomment the blow line
|
||||
// Don't worry, when the below line exists, the lint won't pass: ineffectual assignment to gothUser (ineffassign)
|
||||
// gothUser, ok = goth.User{Email: "invalid-email", Name: "."}, true // intentionally use invalid data to avoid pass the registration check
|
||||
// linkAccountData = &LinkAccountData{authSource, gothUser} // intentionally use invalid data to avoid pass the registration check
|
||||
|
||||
if !ok {
|
||||
if linkAccountData == nil {
|
||||
// no account in session, so just redirect to the login page, then the user could restart the process
|
||||
ctx.Redirect(setting.AppSubURL + "/user/login")
|
||||
return
|
||||
}
|
||||
|
||||
if missingFields, ok := gothUser.RawData["__giteaAutoRegMissingFields"].([]string); ok {
|
||||
ctx.Data["AutoRegistrationFailedPrompt"] = ctx.Tr("auth.oauth_callback_unable_auto_reg", gothUser.Provider, strings.Join(missingFields, ","))
|
||||
if missingFields, ok := linkAccountData.GothUser.RawData["__giteaAutoRegMissingFields"].([]string); ok {
|
||||
ctx.Data["AutoRegistrationFailedPrompt"] = ctx.Tr("auth.oauth_callback_unable_auto_reg", linkAccountData.GothUser.Provider, strings.Join(missingFields, ","))
|
||||
}
|
||||
|
||||
uname, err := extractUserNameFromOAuth2(&gothUser)
|
||||
uname, err := extractUserNameFromOAuth2(&linkAccountData.GothUser)
|
||||
if err != nil {
|
||||
ctx.ServerError("UserSignIn", err)
|
||||
return
|
||||
}
|
||||
email := gothUser.Email
|
||||
email := linkAccountData.GothUser.Email
|
||||
ctx.Data["user_name"] = uname
|
||||
ctx.Data["email"] = email
|
||||
|
||||
@@ -152,8 +149,8 @@ func LinkAccountPostSignIn(ctx *context.Context) {
|
||||
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin"
|
||||
ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/link_account_signup"
|
||||
|
||||
gothUser := ctx.Session.Get("linkAccountGothUser")
|
||||
if gothUser == nil {
|
||||
linkAccountData := oauth2GetLinkAccountData(ctx)
|
||||
if linkAccountData == nil {
|
||||
ctx.ServerError("UserSignIn", errors.New("not in LinkAccount session"))
|
||||
return
|
||||
}
|
||||
@@ -169,11 +166,14 @@ func LinkAccountPostSignIn(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
linkAccount(ctx, u, gothUser.(goth.User), signInForm.Remember)
|
||||
oauth2LinkAccount(ctx, u, linkAccountData, signInForm.Remember)
|
||||
}
|
||||
|
||||
func linkAccount(ctx *context.Context, u *user_model.User, gothUser goth.User, remember bool) {
|
||||
updateAvatarIfNeed(ctx, gothUser.AvatarURL, u)
|
||||
func oauth2LinkAccount(ctx *context.Context, u *user_model.User, linkAccountData *LinkAccountData, remember bool) {
|
||||
oauth2SignInSync(ctx, &linkAccountData.AuthSource, u, linkAccountData.GothUser)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
// If this user is enrolled in 2FA, we can't sign the user in just yet.
|
||||
// Instead, redirect them to the 2FA authentication page.
|
||||
@@ -185,7 +185,7 @@ func linkAccount(ctx *context.Context, u *user_model.User, gothUser goth.User, r
|
||||
return
|
||||
}
|
||||
|
||||
err = externalaccount.LinkAccountToUser(ctx, u, gothUser)
|
||||
err = externalaccount.LinkAccountToUser(ctx, linkAccountData.AuthSource.ID, u, linkAccountData.GothUser)
|
||||
if err != nil {
|
||||
ctx.ServerError("UserLinkAccount", err)
|
||||
return
|
||||
@@ -243,17 +243,11 @@ func LinkAccountPostRegister(ctx *context.Context) {
|
||||
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin"
|
||||
ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/link_account_signup"
|
||||
|
||||
gothUserInterface := ctx.Session.Get("linkAccountGothUser")
|
||||
if gothUserInterface == nil {
|
||||
linkAccountData := oauth2GetLinkAccountData(ctx)
|
||||
if linkAccountData == nil {
|
||||
ctx.ServerError("UserSignUp", errors.New("not in LinkAccount session"))
|
||||
return
|
||||
}
|
||||
gothUser, ok := gothUserInterface.(goth.User)
|
||||
if !ok {
|
||||
ctx.ServerError("UserSignUp", fmt.Errorf("session linkAccountGothUser type is %t but not goth.User", gothUserInterface))
|
||||
return
|
||||
}
|
||||
|
||||
if ctx.HasError() {
|
||||
ctx.HTML(http.StatusOK, tplLinkAccount)
|
||||
return
|
||||
@@ -296,31 +290,33 @@ func LinkAccountPostRegister(ctx *context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
authSource, err := auth.GetActiveOAuth2SourceByName(ctx, gothUser.Provider)
|
||||
if err != nil {
|
||||
ctx.ServerError("CreateUser", err)
|
||||
return
|
||||
}
|
||||
|
||||
u := &user_model.User{
|
||||
Name: form.UserName,
|
||||
Email: form.Email,
|
||||
Passwd: form.Password,
|
||||
LoginType: auth.OAuth2,
|
||||
LoginSource: authSource.ID,
|
||||
LoginName: gothUser.UserID,
|
||||
LoginSource: linkAccountData.AuthSource.ID,
|
||||
LoginName: linkAccountData.GothUser.UserID,
|
||||
}
|
||||
|
||||
if !createAndHandleCreatedUser(ctx, tplLinkAccount, form, u, nil, &gothUser, false) {
|
||||
if !createAndHandleCreatedUser(ctx, tplLinkAccount, form, u, nil, linkAccountData) {
|
||||
// error already handled
|
||||
return
|
||||
}
|
||||
|
||||
source := authSource.Cfg.(*oauth2.Source)
|
||||
if err := syncGroupsToTeams(ctx, source, &gothUser, u); err != nil {
|
||||
source := linkAccountData.AuthSource.Cfg.(*oauth2.Source)
|
||||
if err := syncGroupsToTeams(ctx, source, &linkAccountData.GothUser, u); err != nil {
|
||||
ctx.ServerError("SyncGroupsToTeams", err)
|
||||
return
|
||||
}
|
||||
|
||||
handleSignIn(ctx, u, false)
|
||||
}
|
||||
|
||||
func linkAccountFromContext(ctx *context.Context, user *user_model.User) error {
|
||||
linkAccountData := oauth2GetLinkAccountData(ctx)
|
||||
if linkAccountData == nil {
|
||||
return errors.New("not in LinkAccount session")
|
||||
}
|
||||
return externalaccount.LinkAccountToUser(ctx, linkAccountData.AuthSource.ID, user, linkAccountData.GothUser)
|
||||
}
|
||||
|
@@ -20,7 +20,6 @@ import (
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/session"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/templates"
|
||||
"code.gitea.io/gitea/modules/web/middleware"
|
||||
source_service "code.gitea.io/gitea/services/auth/source"
|
||||
"code.gitea.io/gitea/services/auth/source/oauth2"
|
||||
@@ -35,9 +34,8 @@ import (
|
||||
|
||||
// SignInOAuth handles the OAuth2 login buttons
|
||||
func SignInOAuth(ctx *context.Context) {
|
||||
provider := ctx.PathParam("provider")
|
||||
|
||||
authSource, err := auth.GetActiveOAuth2SourceByName(ctx, provider)
|
||||
authName := ctx.PathParam("provider")
|
||||
authSource, err := auth.GetActiveOAuth2SourceByAuthName(ctx, authName)
|
||||
if err != nil {
|
||||
ctx.ServerError("SignIn", err)
|
||||
return
|
||||
@@ -74,8 +72,6 @@ func SignInOAuth(ctx *context.Context) {
|
||||
|
||||
// SignInOAuthCallback handles the callback from the given provider
|
||||
func SignInOAuthCallback(ctx *context.Context) {
|
||||
provider := ctx.PathParam("provider")
|
||||
|
||||
if ctx.Req.FormValue("error") != "" {
|
||||
var errorKeyValues []string
|
||||
for k, vv := range ctx.Req.Form {
|
||||
@@ -88,7 +84,8 @@ func SignInOAuthCallback(ctx *context.Context) {
|
||||
}
|
||||
|
||||
// first look if the provider is still active
|
||||
authSource, err := auth.GetActiveOAuth2SourceByName(ctx, provider)
|
||||
authName := ctx.PathParam("provider")
|
||||
authSource, err := auth.GetActiveOAuth2SourceByAuthName(ctx, authName)
|
||||
if err != nil {
|
||||
ctx.ServerError("SignIn", err)
|
||||
return
|
||||
@@ -133,7 +130,7 @@ func SignInOAuthCallback(ctx *context.Context) {
|
||||
if u == nil {
|
||||
if ctx.Doer != nil {
|
||||
// attach user to the current signed-in user
|
||||
err = externalaccount.LinkAccountToUser(ctx, ctx.Doer, gothUser)
|
||||
err = externalaccount.LinkAccountToUser(ctx, authSource.ID, ctx.Doer, gothUser)
|
||||
if err != nil {
|
||||
ctx.ServerError("UserLinkAccount", err)
|
||||
return
|
||||
@@ -174,12 +171,11 @@ func SignInOAuthCallback(ctx *context.Context) {
|
||||
gothUser.RawData = make(map[string]any)
|
||||
}
|
||||
gothUser.RawData["__giteaAutoRegMissingFields"] = missingFields
|
||||
showLinkingLogin(ctx, gothUser)
|
||||
showLinkingLogin(ctx, authSource, gothUser)
|
||||
return
|
||||
}
|
||||
u = &user_model.User{
|
||||
Name: uname,
|
||||
FullName: gothUser.Name,
|
||||
Email: gothUser.Email,
|
||||
LoginType: auth.OAuth2,
|
||||
LoginSource: authSource.ID,
|
||||
@@ -196,7 +192,11 @@ func SignInOAuthCallback(ctx *context.Context) {
|
||||
u.IsAdmin = isAdmin.ValueOrDefault(user_service.UpdateOptionField[bool]{FieldValue: false}).FieldValue
|
||||
u.IsRestricted = isRestricted.ValueOrDefault(setting.Service.DefaultUserIsRestricted)
|
||||
|
||||
if !createAndHandleCreatedUser(ctx, templates.TplName(""), nil, u, overwriteDefault, &gothUser, setting.OAuth2Client.AccountLinking != setting.OAuth2AccountLinkingDisabled) {
|
||||
linkAccountData := &LinkAccountData{*authSource, gothUser}
|
||||
if setting.OAuth2Client.AccountLinking == setting.OAuth2AccountLinkingDisabled {
|
||||
linkAccountData = nil
|
||||
}
|
||||
if !createAndHandleCreatedUser(ctx, "", nil, u, overwriteDefault, linkAccountData) {
|
||||
// error already handled
|
||||
return
|
||||
}
|
||||
@@ -207,7 +207,7 @@ func SignInOAuthCallback(ctx *context.Context) {
|
||||
}
|
||||
} else {
|
||||
// no existing user is found, request attach or new account
|
||||
showLinkingLogin(ctx, gothUser)
|
||||
showLinkingLogin(ctx, authSource, gothUser)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -271,9 +271,22 @@ func getUserAdminAndRestrictedFromGroupClaims(source *oauth2.Source, gothUser *g
|
||||
return isAdmin, isRestricted
|
||||
}
|
||||
|
||||
func showLinkingLogin(ctx *context.Context, gothUser goth.User) {
|
||||
type LinkAccountData struct {
|
||||
AuthSource auth.Source
|
||||
GothUser goth.User
|
||||
}
|
||||
|
||||
func oauth2GetLinkAccountData(ctx *context.Context) *LinkAccountData {
|
||||
v, ok := ctx.Session.Get("linkAccountData").(LinkAccountData)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return &v
|
||||
}
|
||||
|
||||
func showLinkingLogin(ctx *context.Context, authSource *auth.Source, gothUser goth.User) {
|
||||
if err := updateSession(ctx, nil, map[string]any{
|
||||
"linkAccountGothUser": gothUser,
|
||||
"linkAccountData": LinkAccountData{*authSource, gothUser},
|
||||
}); err != nil {
|
||||
ctx.ServerError("updateSession", err)
|
||||
return
|
||||
@@ -281,7 +294,7 @@ func showLinkingLogin(ctx *context.Context, gothUser goth.User) {
|
||||
ctx.Redirect(setting.AppSubURL + "/user/link_account")
|
||||
}
|
||||
|
||||
func updateAvatarIfNeed(ctx *context.Context, url string, u *user_model.User) {
|
||||
func oauth2UpdateAvatarIfNeed(ctx *context.Context, url string, u *user_model.User) {
|
||||
if setting.OAuth2Client.UpdateAvatar && len(url) > 0 {
|
||||
resp, err := http.Get(url)
|
||||
if err == nil {
|
||||
@@ -299,11 +312,14 @@ func updateAvatarIfNeed(ctx *context.Context, url string, u *user_model.User) {
|
||||
}
|
||||
}
|
||||
|
||||
func handleOAuth2SignIn(ctx *context.Context, source *auth.Source, u *user_model.User, gothUser goth.User) {
|
||||
updateAvatarIfNeed(ctx, gothUser.AvatarURL, u)
|
||||
func handleOAuth2SignIn(ctx *context.Context, authSource *auth.Source, u *user_model.User, gothUser goth.User) {
|
||||
oauth2SignInSync(ctx, authSource, u, gothUser)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
needs2FA := false
|
||||
if !source.TwoFactorShouldSkip() {
|
||||
if !authSource.TwoFactorShouldSkip() {
|
||||
_, err := auth.GetTwoFactorByUID(ctx, u.ID)
|
||||
if err != nil && !auth.IsErrTwoFactorNotEnrolled(err) {
|
||||
ctx.ServerError("UserSignIn", err)
|
||||
@@ -312,7 +328,7 @@ func handleOAuth2SignIn(ctx *context.Context, source *auth.Source, u *user_model
|
||||
needs2FA = err == nil
|
||||
}
|
||||
|
||||
oauth2Source := source.Cfg.(*oauth2.Source)
|
||||
oauth2Source := authSource.Cfg.(*oauth2.Source)
|
||||
groupTeamMapping, err := auth_module.UnmarshalGroupTeamMapping(oauth2Source.GroupTeamMap)
|
||||
if err != nil {
|
||||
ctx.ServerError("UnmarshalGroupTeamMapping", err)
|
||||
@@ -338,7 +354,7 @@ func handleOAuth2SignIn(ctx *context.Context, source *auth.Source, u *user_model
|
||||
}
|
||||
}
|
||||
|
||||
if err := externalaccount.EnsureLinkExternalToUser(ctx, u, gothUser); err != nil {
|
||||
if err := externalaccount.EnsureLinkExternalToUser(ctx, authSource.ID, u, gothUser); err != nil {
|
||||
ctx.ServerError("EnsureLinkExternalToUser", err)
|
||||
return
|
||||
}
|
||||
|
88
routers/web/auth/oauth_signin_sync.go
Normal file
88
routers/web/auth/oauth_signin_sync.go
Normal file
@@ -0,0 +1,88 @@
|
||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package auth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||
"code.gitea.io/gitea/models/auth"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
asymkey_service "code.gitea.io/gitea/services/asymkey"
|
||||
"code.gitea.io/gitea/services/auth/source/oauth2"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
|
||||
"github.com/markbates/goth"
|
||||
)
|
||||
|
||||
func oauth2SignInSync(ctx *context.Context, authSource *auth.Source, u *user_model.User, gothUser goth.User) {
|
||||
oauth2UpdateAvatarIfNeed(ctx, gothUser.AvatarURL, u)
|
||||
|
||||
oauth2Source, _ := authSource.Cfg.(*oauth2.Source)
|
||||
if !authSource.IsOAuth2() || oauth2Source == nil {
|
||||
ctx.ServerError("oauth2SignInSync", fmt.Errorf("source %s is not an OAuth2 source", gothUser.Provider))
|
||||
return
|
||||
}
|
||||
|
||||
// sync full name
|
||||
fullNameKey := util.IfZero(oauth2Source.FullNameClaimName, "name")
|
||||
fullName, _ := gothUser.RawData[fullNameKey].(string)
|
||||
fullName = util.IfZero(fullName, gothUser.Name)
|
||||
|
||||
// need to update if the user has no full name set
|
||||
shouldUpdateFullName := u.FullName == ""
|
||||
// force to update if the attribute is set
|
||||
shouldUpdateFullName = shouldUpdateFullName || oauth2Source.FullNameClaimName != ""
|
||||
// only update if the full name is different
|
||||
shouldUpdateFullName = shouldUpdateFullName && u.FullName != fullName
|
||||
if shouldUpdateFullName {
|
||||
u.FullName = fullName
|
||||
if err := user_model.UpdateUserCols(ctx, u, "full_name"); err != nil {
|
||||
log.Error("Unable to sync OAuth2 user full name %s: %v", gothUser.Provider, err)
|
||||
}
|
||||
}
|
||||
|
||||
err := oauth2UpdateSSHPubIfNeed(ctx, authSource, &gothUser, u)
|
||||
if err != nil {
|
||||
log.Error("Unable to sync OAuth2 SSH public key %s: %v", gothUser.Provider, err)
|
||||
}
|
||||
}
|
||||
|
||||
func oauth2SyncGetSSHKeys(source *oauth2.Source, gothUser *goth.User) ([]string, error) {
|
||||
value, exists := gothUser.RawData[source.SSHPublicKeyClaimName]
|
||||
if !exists {
|
||||
return []string{}, nil
|
||||
}
|
||||
rawSlice, ok := value.([]any)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid SSH public key value type: %T", value)
|
||||
}
|
||||
|
||||
sshKeys := make([]string, 0, len(rawSlice))
|
||||
for _, v := range rawSlice {
|
||||
str, ok := v.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid SSH public key value item type: %T", v)
|
||||
}
|
||||
sshKeys = append(sshKeys, str)
|
||||
}
|
||||
return sshKeys, nil
|
||||
}
|
||||
|
||||
func oauth2UpdateSSHPubIfNeed(ctx *context.Context, authSource *auth.Source, gothUser *goth.User, user *user_model.User) error {
|
||||
oauth2Source, _ := authSource.Cfg.(*oauth2.Source)
|
||||
if oauth2Source == nil || oauth2Source.SSHPublicKeyClaimName == "" {
|
||||
return nil
|
||||
}
|
||||
sshKeys, err := oauth2SyncGetSSHKeys(oauth2Source, gothUser)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !asymkey_model.SynchronizePublicKeys(ctx, user, authSource, sshKeys) {
|
||||
return nil
|
||||
}
|
||||
return asymkey_service.RewriteAllPublicKeys(ctx)
|
||||
}
|
@@ -361,7 +361,7 @@ func RegisterOpenIDPost(ctx *context.Context) {
|
||||
Email: form.Email,
|
||||
Passwd: password,
|
||||
}
|
||||
if !createUserInContext(ctx, tplSignUpOID, form, u, nil, nil, false) {
|
||||
if !createUserInContext(ctx, tplSignUpOID, form, u, nil, nil) {
|
||||
// error already handled
|
||||
return
|
||||
}
|
||||
|
@@ -15,7 +15,6 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/templates"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
"code.gitea.io/gitea/services/externalaccount"
|
||||
|
||||
"github.com/go-webauthn/webauthn/protocol"
|
||||
"github.com/go-webauthn/webauthn/webauthn"
|
||||
@@ -150,7 +149,7 @@ func WebAuthnPasskeyLogin(ctx *context.Context) {
|
||||
|
||||
// Now handle account linking if that's requested
|
||||
if ctx.Session.Get("linkAccount") != nil {
|
||||
if err := externalaccount.LinkAccountFromStore(ctx, ctx.Session, user); err != nil {
|
||||
if err := linkAccountFromContext(ctx, user); err != nil {
|
||||
ctx.ServerError("LinkAccountFromStore", err)
|
||||
return
|
||||
}
|
||||
@@ -268,7 +267,7 @@ func WebAuthnLoginAssertionPost(ctx *context.Context) {
|
||||
|
||||
// Now handle account linking if that's requested
|
||||
if ctx.Session.Get("linkAccount") != nil {
|
||||
if err := externalaccount.LinkAccountFromStore(ctx, ctx.Session, user); err != nil {
|
||||
if err := linkAccountFromContext(ctx, user); err != nil {
|
||||
ctx.ServerError("LinkAccountFromStore", err)
|
||||
return
|
||||
}
|
||||
|
Reference in New Issue
Block a user