fix(auth): keep login state on rate-limited or failing token refresh

When the dashboard token refresh endpoint returned 429 (shared
critical rate limit) the frontend classified it as out_of_sync,
cleared local auth state, and redirected to /sign-in. The rate limit
itself is working as intended; the bug is that a temporary rejection
was treated as a terminal auth failure.

- Treat 429 refresh responses as transient errors on the frontend,
  keeping the session retryable instead of clearing it. Only explicit
  401 or confirmed session mismatch/race exhaustion clears auth state.
- Return Retry-After on all rate-limited responses (remaining TTL on
  Redis, window duration on the in-memory limiter) so clients can
  back off.
- Log the underlying error with request context when auth session
  errors map to 500 AUTH_INTERNAL_ERROR, and replace fmt.Println with
  request-scoped logging in the Redis rate limiter error paths.

Fixes #6361
This commit is contained in:
CaIon
2026-07-21 12:39:29 +08:00
parent e0d5156115
commit 1721144221
5 changed files with 57 additions and 14 deletions
+8
View File
@@ -2,9 +2,11 @@ package controller
import (
"errors"
"fmt"
"net/http"
"strings"
"github.com/QuantumNous/new-api/logger"
"github.com/QuantumNous/new-api/middleware"
"github.com/QuantumNous/new-api/model"
"github.com/QuantumNous/new-api/service"
@@ -164,6 +166,12 @@ func writeAuthSessionError(c *gin.Context, err error) {
if errors.Is(err, gorm.ErrRecordNotFound) {
status, code = http.StatusUnauthorized, "AUTH_UNAUTHORIZED"
}
if status == http.StatusInternalServerError {
// The response body only carries the generic AUTH_INTERNAL_ERROR
// code; without this log the underlying Redis/database/session
// failure is indistinguishable from the client side.
logger.LogError(c.Request.Context(), fmt.Sprintf("auth session internal error (%s %s): %v", c.Request.Method, c.Request.URL.Path, err))
}
c.JSON(status, gin.H{"success": false, "code": code, "message": http.StatusText(status)})
}