fix(auth): improve token refresh with better logging and error handling
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 49s

- Add detailed logging for token refresh process
- Increase refresh timeout from 5s to 10s
- Log response body on refresh failure for diagnostics
- Verify tokens are present in refresh response
- Improve authFetch logging during retry

Version: 3.9.4
This commit is contained in:
poignatov
2026-01-12 17:05:19 +03:00
parent b3a83e1e8f
commit 3cf3cd4edb
3 changed files with 31 additions and 5 deletions

View File

@@ -1 +1 @@
3.9.3 3.9.4

View File

@@ -1,6 +1,6 @@
{ {
"name": "play-life-web", "name": "play-life-web",
"version": "3.9.2", "version": "3.9.4",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",

View File

@@ -42,12 +42,15 @@ export function AuthProvider({ children }) {
const refresh = localStorage.getItem(REFRESH_TOKEN_KEY) const refresh = localStorage.getItem(REFRESH_TOKEN_KEY)
if (!refresh) { if (!refresh) {
console.warn('[Auth] No refresh token in localStorage')
return { success: false, isNetworkError: false } return { success: false, isNetworkError: false }
} }
console.log('[Auth] Attempting refresh with token:', refresh.substring(0, 10) + '...')
try { try {
const controller = new AbortController() const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), 5000) // 5 second timeout const timeoutId = setTimeout(() => controller.abort(), 10000) // 10 second timeout (increased)
const response = await fetch('/api/auth/refresh', { const response = await fetch('/api/auth/refresh', {
method: 'POST', method: 'POST',
@@ -61,6 +64,15 @@ export function AuthProvider({ children }) {
clearTimeout(timeoutId) clearTimeout(timeoutId)
if (!response.ok) { if (!response.ok) {
// Логируем тело ответа для диагностики
let errorBody = ''
try {
errorBody = await response.text()
} catch (e) {
errorBody = 'Could not read error body'
}
console.error('[Auth] Refresh failed:', response.status, errorBody)
// 401 means invalid token (real auth error) // 401 means invalid token (real auth error)
// Other errors might be temporary (503, 502, etc.) // Other errors might be temporary (503, 502, etc.)
const isAuthError = response.status === 401 const isAuthError = response.status === 401
@@ -69,6 +81,13 @@ export function AuthProvider({ children }) {
const data = await response.json() const data = await response.json()
// Проверяем что токены действительно пришли
if (!data.access_token || !data.refresh_token) {
console.error('[Auth] Refresh response missing tokens:', Object.keys(data))
return { success: false, isNetworkError: false }
}
console.log('[Auth] Refresh successful, saving new tokens')
localStorage.setItem(TOKEN_KEY, data.access_token) localStorage.setItem(TOKEN_KEY, data.access_token)
localStorage.setItem(REFRESH_TOKEN_KEY, data.refresh_token) localStorage.setItem(REFRESH_TOKEN_KEY, data.refresh_token)
localStorage.setItem(USER_KEY, JSON.stringify(data.user)) localStorage.setItem(USER_KEY, JSON.stringify(data.user))
@@ -76,14 +95,14 @@ export function AuthProvider({ children }) {
return { success: true, isNetworkError: false } return { success: true, isNetworkError: false }
} catch (err) { } catch (err) {
console.error('[Auth] Refresh error:', err.name, err.message)
// Network errors should be treated as temporary // Network errors should be treated as temporary
if (err.name === 'AbortError' || if (err.name === 'AbortError' ||
(err.name === 'TypeError' && (err.message.includes('fetch') || err.message.includes('Failed to fetch')))) { (err.name === 'TypeError' && (err.message.includes('fetch') || err.message.includes('Failed to fetch')))) {
console.warn('Refresh token network error, keeping session:', err.message) console.warn('[Auth] Refresh token network error, keeping session')
return { success: false, isNetworkError: true } return { success: false, isNetworkError: true }
} }
// Other errors might be auth related // Other errors might be auth related
console.error('Refresh token error:', err)
return { success: false, isNetworkError: false } return { success: false, isNetworkError: false }
} }
}, []) }, [])
@@ -276,14 +295,20 @@ export function AuthProvider({ children }) {
// If 401, try to refresh token and retry // If 401, try to refresh token and retry
if (response.status === 401) { if (response.status === 401) {
console.log('[Auth] Got 401 for', url, '- attempting token refresh')
const result = await refreshToken() const result = await refreshToken()
if (result.success) { if (result.success) {
console.log('[Auth] Token refreshed, retrying request to', url)
const newToken = localStorage.getItem(TOKEN_KEY) const newToken = localStorage.getItem(TOKEN_KEY)
headers['Authorization'] = `Bearer ${newToken}` headers['Authorization'] = `Bearer ${newToken}`
response = await fetch(url, { ...options, headers }) response = await fetch(url, { ...options, headers })
console.log('[Auth] Retry response status:', response.status)
} else if (!result.isNetworkError) { } else if (!result.isNetworkError) {
// Only logout if refresh failed due to auth error (not network error) // Only logout if refresh failed due to auth error (not network error)
console.warn('[Auth] Refresh failed with auth error, logging out')
logout() logout()
} else {
console.warn('[Auth] Refresh failed with network error, keeping session but request failed')
} }
// If network error, don't logout - let the caller handle the 401 // If network error, don't logout - let the caller handle the 401
} }
@@ -292,6 +317,7 @@ export function AuthProvider({ children }) {
} catch (err) { } catch (err) {
// Network errors should not trigger logout // Network errors should not trigger logout
// Let the caller handle the error // Let the caller handle the error
console.error('[Auth] Fetch error for', url, ':', err.message)
throw err throw err
} }
}, [refreshToken, logout]) }, [refreshToken, logout])