fix(auth): preserve resolved token version on oauth login
This commit is contained in:
@@ -778,6 +778,14 @@ func TestExchangePendingOAuthCompletionExistingLoginWithSuggestedProfileSkipsAdo
|
||||
require.Equal(t, "https://cdn.example/existing-login.png", payload["suggested_avatar_url"])
|
||||
require.NotContains(t, payload, "adoption_required")
|
||||
|
||||
accessToken, ok := payload["access_token"].(string)
|
||||
require.True(t, ok)
|
||||
claims, err := handler.authService.ValidateToken(accessToken)
|
||||
require.NoError(t, err)
|
||||
reloadedUser, err := handler.userService.GetByID(ctx, userEntity.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, reloadedUser.TokenVersion, claims.TokenVersion)
|
||||
|
||||
decisionCount, err := client.IdentityAdoptionDecision.Query().
|
||||
Where(identityadoptiondecision.PendingAuthSessionIDEQ(session.ID)).
|
||||
Count(ctx)
|
||||
@@ -2033,6 +2041,13 @@ func TestLogin2FACompletesPendingOAuthBindAndConsumesSession(t *testing.T) {
|
||||
payload := decodeJSONResponseData(t, recorder)
|
||||
require.NotEmpty(t, payload["access_token"])
|
||||
require.NotEmpty(t, payload["refresh_token"])
|
||||
accessToken, ok := payload["access_token"].(string)
|
||||
require.True(t, ok)
|
||||
claims, err := handler.authService.ValidateToken(accessToken)
|
||||
require.NoError(t, err)
|
||||
reloadedUser, err := handler.userService.GetByID(ctx, existingUser.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, reloadedUser.TokenVersion, claims.TokenVersion)
|
||||
|
||||
identity, err := client.AuthIdentity.Query().
|
||||
Where(
|
||||
|
||||
@@ -1500,6 +1500,9 @@ func resolvedTokenVersion(user *User) int64 {
|
||||
if user == nil {
|
||||
return 0
|
||||
}
|
||||
if user.TokenVersionResolved {
|
||||
return user.TokenVersion
|
||||
}
|
||||
|
||||
material := strings.ToLower(strings.TrimSpace(user.Email)) + "\n" + user.PasswordHash
|
||||
sum := sha256.Sum256([]byte(material))
|
||||
|
||||
@@ -23,12 +23,15 @@ type User struct {
|
||||
Status string
|
||||
AllowedGroups []int64
|
||||
TokenVersion int64 // Incremented on password change to invalidate existing tokens
|
||||
SignupSource string
|
||||
LastLoginAt *time.Time
|
||||
LastActiveAt *time.Time
|
||||
LastUsedAt *time.Time
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
// TokenVersionResolved indicates TokenVersion already contains the fingerprint-derived
|
||||
// value expected in JWT claims and refresh-token state.
|
||||
TokenVersionResolved bool
|
||||
SignupSource string
|
||||
LastLoginAt *time.Time
|
||||
LastActiveAt *time.Time
|
||||
LastUsedAt *time.Time
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
|
||||
// GroupRates 用户专属分组倍率配置
|
||||
// map[groupID]rateMultiplier
|
||||
|
||||
@@ -943,10 +943,11 @@ func (s *UserService) GetByID(ctx context.Context, id int64) (*User, error) {
|
||||
}
|
||||
|
||||
func normalizeLoadedUserTokenVersion(user *User) {
|
||||
if user == nil {
|
||||
if user == nil || user.TokenVersionResolved {
|
||||
return
|
||||
}
|
||||
user.TokenVersion = resolvedTokenVersion(user)
|
||||
user.TokenVersionResolved = true
|
||||
}
|
||||
|
||||
// TouchLastActive 通过防抖更新 users.last_active_at,减少鉴权热路径写放大。
|
||||
|
||||
Reference in New Issue
Block a user