From 6eed13c412bc0aeb1d75873319fedabd7482d01f Mon Sep 17 00:00:00 2001 From: liuyin <35710592@qq.com> Date: Tue, 21 Apr 2026 21:13:50 +0800 Subject: [PATCH 1/3] fix(auth): create default token for third-party registrations Ensure users created through OAuth/OIDC and WeChat registration receive the same default token as password registrations when GENERATE_DEFAULT_TOKEN is enabled. Constraint: Keep returning users from receiving duplicate default tokens on login Rejected: Duplicating token creation logic in each registration path | would keep behavior divergent across auth flows Confidence: high Scope-risk: narrow --- controller/oauth.go | 36 ++++++++++++++++-------- controller/user.go | 66 ++++++++++++++++++++++++++++---------------- controller/wechat.go | 11 ++++++++ 3 files changed, 78 insertions(+), 35 deletions(-) diff --git a/controller/oauth.go b/controller/oauth.go index 9951f22b035f..d886700ad0a9 100644 --- a/controller/oauth.go +++ b/controller/oauth.go @@ -104,7 +104,7 @@ func HandleOAuth(c *gin.Context) { } // 7. Find or create user - user, err := findOrCreateOAuthUser(c, provider, oauthUser, session) + user, created, err := findOrCreateOAuthUser(c, provider, oauthUser, session) if err != nil { switch err.(type) { case *OAuthUserDeletedError: @@ -117,6 +117,20 @@ func HandleOAuth(c *gin.Context) { return } + if created { + if err := createDefaultTokenForUser(user.Id, user.Username); err != nil { + switch err { + case errGenerateDefaultTokenKey: + common.ApiErrorI18n(c, i18n.MsgUserDefaultTokenFailed) + case errCreateDefaultToken: + common.ApiErrorI18n(c, i18n.MsgCreateDefaultTokenErr) + default: + common.ApiError(c, err) + } + return + } + } + // 8. Check user status if user.Status != common.UserStatusEnabled { common.ApiErrorI18n(c, i18n.MsgOAuthUserBanned) @@ -196,20 +210,20 @@ func handleOAuthBind(c *gin.Context, provider oauth.Provider) { } // findOrCreateOAuthUser finds existing user or creates new user -func findOrCreateOAuthUser(c *gin.Context, provider oauth.Provider, oauthUser *oauth.OAuthUser, session sessions.Session) (*model.User, error) { +func findOrCreateOAuthUser(c *gin.Context, provider oauth.Provider, oauthUser *oauth.OAuthUser, session sessions.Session) (*model.User, bool, error) { user := &model.User{} // Check if user already exists with new ID if provider.IsUserIDTaken(oauthUser.ProviderUserID) { err := provider.FillUserByProviderID(user, oauthUser.ProviderUserID) if err != nil { - return nil, err + return nil, false, err } // Check if user has been deleted if user.Id == 0 { - return nil, &OAuthUserDeletedError{} + return nil, false, &OAuthUserDeletedError{} } - return user, nil + return user, false, nil } // Try to find user with legacy ID (for GitHub migration from login to numeric ID) @@ -217,7 +231,7 @@ func findOrCreateOAuthUser(c *gin.Context, provider oauth.Provider, oauthUser *o if provider.IsUserIDTaken(legacyID) { err := provider.FillUserByProviderID(user, legacyID) if err != nil { - return nil, err + return nil, false, err } if user.Id != 0 { // Found user with legacy ID, migrate to new ID @@ -227,14 +241,14 @@ func findOrCreateOAuthUser(c *gin.Context, provider oauth.Provider, oauthUser *o common.SysError(fmt.Sprintf("[OAuth] Failed to migrate user %d: %s", user.Id, err.Error())) // Continue with login even if migration fails } - return user, nil + return user, false, nil } } } // User doesn't exist, create new user if registration is enabled if !common.RegisterEnabled { - return nil, &OAuthRegistrationDisabledError{} + return nil, false, &OAuthRegistrationDisabledError{} } // Set up new user @@ -291,7 +305,7 @@ func findOrCreateOAuthUser(c *gin.Context, provider oauth.Provider, oauthUser *o return nil }) if err != nil { - return nil, err + return nil, false, err } // Perform post-transaction tasks (logs, sidebar config, inviter rewards) @@ -320,14 +334,14 @@ func findOrCreateOAuthUser(c *gin.Context, provider oauth.Provider, oauthUser *o return nil }) if err != nil { - return nil, err + return nil, false, err } // Perform post-transaction tasks user.FinalizeOAuthUserCreation(inviterId) } - return user, nil + return user, true, nil } // Error types for OAuth diff --git a/controller/user.go b/controller/user.go index d6becdd8f0e9..4998f8bb5fd7 100644 --- a/controller/user.go +++ b/controller/user.go @@ -116,6 +116,41 @@ func setupLogin(user *model.User, c *gin.Context) { }) } +var ( + errGenerateDefaultTokenKey = errors.New("failed to generate default token key") + errCreateDefaultToken = errors.New("failed to create default token") +) + +func createDefaultTokenForUser(userId int, username string) error { + if !constant.GenerateDefaultToken { + return nil + } + key, err := common.GenerateKey() + if err != nil { + common.SysLog("failed to generate token key: " + err.Error()) + return errGenerateDefaultTokenKey + } + now := common.GetTimestamp() + token := model.Token{ + UserId: userId, + Name: username + "的初始令牌", + Key: key, + CreatedTime: now, + AccessedTime: now, + ExpiredTime: -1, + RemainQuota: 500000, + UnlimitedQuota: true, + ModelLimitsEnabled: false, + } + if setting.DefaultUseAutoGroup { + token.Group = "auto" + } + if err := token.Insert(); err != nil { + return errCreateDefaultToken + } + return nil +} + func Logout(c *gin.Context) { session := sessions.Default(c) session.Clear() @@ -195,33 +230,16 @@ func Register(c *gin.Context) { common.ApiErrorI18n(c, i18n.MsgUserRegisterFailed) return } - // 生成默认令牌 - if constant.GenerateDefaultToken { - key, err := common.GenerateKey() - if err != nil { + if err := createDefaultTokenForUser(insertedUser.Id, cleanUser.Username); err != nil { + switch err { + case errGenerateDefaultTokenKey: common.ApiErrorI18n(c, i18n.MsgUserDefaultTokenFailed) - common.SysLog("failed to generate token key: " + err.Error()) - return - } - // 生成默认令牌 - token := model.Token{ - UserId: insertedUser.Id, // 使用插入后的用户ID - Name: cleanUser.Username + "的初始令牌", - Key: key, - CreatedTime: common.GetTimestamp(), - AccessedTime: common.GetTimestamp(), - ExpiredTime: -1, // 永不过期 - RemainQuota: 500000, // 示例额度 - UnlimitedQuota: true, - ModelLimitsEnabled: false, - } - if setting.DefaultUseAutoGroup { - token.Group = "auto" - } - if err := token.Insert(); err != nil { + case errCreateDefaultToken: common.ApiErrorI18n(c, i18n.MsgCreateDefaultTokenErr) - return + default: + common.ApiError(c, err) } + return } c.JSON(http.StatusOK, gin.H{ diff --git a/controller/wechat.go b/controller/wechat.go index 8889daca77db..8a1c245bbed5 100644 --- a/controller/wechat.go +++ b/controller/wechat.go @@ -103,6 +103,17 @@ func WeChatAuth(c *gin.Context) { }) return } + if err := createDefaultTokenForUser(user.Id, user.Username); err != nil { + switch err { + case errGenerateDefaultTokenKey: + common.ApiErrorMsg(c, "默认令牌生成失败") + case errCreateDefaultToken: + common.ApiErrorMsg(c, "默认令牌创建失败") + default: + common.ApiError(c, err) + } + return + } } else { c.JSON(http.StatusOK, gin.H{ "success": false, From fa605f4cff33827141877e524555548fd435c78c Mon Sep 17 00:00:00 2001 From: liuyin <35710592@qq.com> Date: Tue, 21 Apr 2026 21:25:35 +0800 Subject: [PATCH 2/3] fix(auth): harden default token error handling Use errors.Is for default token creation failures, log token insert errors for operators, and keep WeChat registration error messaging localized. Constraint: Keep the PR scoped to the third-party registration token fix Rejected: Expanding the flow to auto-retry or silently continue after token creation failure | changes product behavior beyond this bug fix Confidence: high Scope-risk: narrow --- controller/oauth.go | 6 +++--- controller/user.go | 9 +++++---- controller/wechat.go | 11 ++++++----- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/controller/oauth.go b/controller/oauth.go index d886700ad0a9..2f75328c3169 100644 --- a/controller/oauth.go +++ b/controller/oauth.go @@ -119,10 +119,10 @@ func HandleOAuth(c *gin.Context) { if created { if err := createDefaultTokenForUser(user.Id, user.Username); err != nil { - switch err { - case errGenerateDefaultTokenKey: + switch { + case errors.Is(err, errGenerateDefaultTokenKey): common.ApiErrorI18n(c, i18n.MsgUserDefaultTokenFailed) - case errCreateDefaultToken: + case errors.Is(err, errCreateDefaultToken): common.ApiErrorI18n(c, i18n.MsgCreateDefaultTokenErr) default: common.ApiError(c, err) diff --git a/controller/user.go b/controller/user.go index 4998f8bb5fd7..ac12f6234f85 100644 --- a/controller/user.go +++ b/controller/user.go @@ -146,7 +146,8 @@ func createDefaultTokenForUser(userId int, username string) error { token.Group = "auto" } if err := token.Insert(); err != nil { - return errCreateDefaultToken + common.SysLog(fmt.Sprintf("failed to insert default token for user %d: %s", userId, err.Error())) + return fmt.Errorf("insert default token: %w", errCreateDefaultToken) } return nil } @@ -231,10 +232,10 @@ func Register(c *gin.Context) { return } if err := createDefaultTokenForUser(insertedUser.Id, cleanUser.Username); err != nil { - switch err { - case errGenerateDefaultTokenKey: + switch { + case errors.Is(err, errGenerateDefaultTokenKey): common.ApiErrorI18n(c, i18n.MsgUserDefaultTokenFailed) - case errCreateDefaultToken: + case errors.Is(err, errCreateDefaultToken): common.ApiErrorI18n(c, i18n.MsgCreateDefaultTokenErr) default: common.ApiError(c, err) diff --git a/controller/wechat.go b/controller/wechat.go index 8a1c245bbed5..292306fc8dda 100644 --- a/controller/wechat.go +++ b/controller/wechat.go @@ -10,6 +10,7 @@ import ( "time" "github.com/QuantumNous/new-api/common" + "github.com/QuantumNous/new-api/i18n" "github.com/QuantumNous/new-api/model" "github.com/gin-contrib/sessions" @@ -104,11 +105,11 @@ func WeChatAuth(c *gin.Context) { return } if err := createDefaultTokenForUser(user.Id, user.Username); err != nil { - switch err { - case errGenerateDefaultTokenKey: - common.ApiErrorMsg(c, "默认令牌生成失败") - case errCreateDefaultToken: - common.ApiErrorMsg(c, "默认令牌创建失败") + switch { + case errors.Is(err, errGenerateDefaultTokenKey): + common.ApiErrorI18n(c, i18n.MsgUserDefaultTokenFailed) + case errors.Is(err, errCreateDefaultToken): + common.ApiErrorI18n(c, i18n.MsgCreateDefaultTokenErr) default: common.ApiError(c, err) } From f181a1e9bc02018a2a47a2a841a6590d4ea4bfc9 Mon Sep 17 00:00:00 2001 From: liuyin <35710592@qq.com> Date: Tue, 21 Apr 2026 21:35:13 +0800 Subject: [PATCH 3/3] fix(auth): add missing oauth errors import Add the standard errors import required by the new errors.Is checks in the OAuth default token flow. Constraint: Keep the follow-up scoped to the review-reported compile issue Confidence: high Scope-risk: narrow --- controller/oauth.go | 1 + 1 file changed, 1 insertion(+) diff --git a/controller/oauth.go b/controller/oauth.go index 2f75328c3169..b39b4ec04def 100644 --- a/controller/oauth.go +++ b/controller/oauth.go @@ -1,6 +1,7 @@ package controller import ( + "errors" "fmt" "net/http" "strconv"