# 429 Too Many Requests как причина деавторизации **Контекст:** При быстрых F5 (3-4 раза) на `/places/my` пользователя деавторизует — `/auth/me` начинает стабильно падать с 401. ## Суть Коренная причина: **write-лимитер (burst=10) исчерпывается быстрее, чем refresh-механизм успевает восстановить сессию.** ### Схема race при старом коде 1. Page load → `refreshSession()` вызывает `/auth/me` **без Bearer** → 401 2. Page load → `fetchPlaces()` вызывает `/places/my` **без Bearer** → 401 3. Оба 401 попадают в `request()` → `catch` → `refreshAccessToken()` + ретрай с Bearer 4. Ретрай `/auth/me` с Bearer → 200 (write limiter: +1) 5. Ретрай `/places/my` с Bearer → 200 (write limiter: +1) 6. **Итого на 1 page load: 2 запроса к write-лимитеру.** Burst=10 → исчерпан за 5 page loads ### Дополнительный эффект: каскад 401 → 429 Когда burst исчерпан: - `/auth/me` → 429 (Too Many Requests) - `request()` в `catch` → деавторизация - Пользователь видит "Войти" ## Исправления ### 1. `refreshSession` — сначала refresh, потом `/auth/me` `frontend/src/hooks/useAuth.tsx`: ```ts // было: const res = await api.get('/auth/me') // стало: const newToken = await refreshAccessToken() setAccessToken(newToken) const res = await api.get('/auth/me') ``` Теперь `/auth/me` приходит с Bearer с первого раза — ни 401, ни ретрая. Сокращает write-лимитер с 2 до 1 хита на page load. ### 2. Экспорт `refreshAccessToken` из `api.ts` Нужен для прямого вызова из `refreshSession()`. ### 3. 429 retry в `request()` `frontend/src/lib/api.ts`: ```ts if (res.status === 429 && !isRetry) { await new Promise(r => setTimeout(r, 1000)) return request(path, options, true) } ``` ### 4. Увеличение write-лимитера 10 → 60 req/min - `backend/cmd/api/main.go` (in-memory fallback): `10/60.0, 10` → `60/60.0, 60` - `backend/internal/middleware/ratelimit_redis.go` (Redis): `Limit: 10` → `Limit: 60` Файлы: - `frontend/src/hooks/useAuth.tsx:38-41` - `frontend/src/lib/api.ts:19` (экспорт) - `frontend/src/lib/api.ts:72-75` (429 retry) - `backend/cmd/api/main.go:149` - `backend/internal/middleware/ratelimit_redis.go:146` ## Связанные заметки - [[atomic-refresh-token-race-condition]] - [[atomic-cookie-secure-via-protocol]] - [[atomic-fetch-retry-infinite-loop]] - [[atomic-redis-rate-limiter-failopen-failclosed]] #auth #rate-limiting #frontend #backend #bugfix