Andrey Epifancev edaed04a03 fix(auth): session no longer drops after access-token expiry
Access-токен жил 60 минут без обновления: фронт не слал response-interceptor
на 401, refresh-токен лежал в localStorage неиспользуемым, WebSocket-сокеты
(форма, статусы выгрузки) не переподключались после закрытия с auth-кодом,
а после редиректа на /login пользователь терял исходную страницу.

- tokenRefresh.js: чистые getTokenExp/msUntilRefresh/createSingleFlight (TDD).
- auth.js: refreshTokens — обновление токена вне интерсепторов api (без
  рекурсии), централизованно чистит токены и шлёт auth:expired при неудаче.
- client.js: response-interceptor повторяет запрос после обновления токена.
- AuthProvider: при старте и по 'auth:expired'/'auth:refreshed' не выкидывает
  из системы, пока жив refresh; проактивный таймер обновления + visibilitychange.
- Routes/LoginPage: PrivateRoute сохраняет исходный location, вход возвращает
  туда же вместо /tasks; убрана мёртвая ветка loading.
- useWebSocket/exports: переподключение после 1008/4401 с одной попыткой
  refreshTokens() перед тем как сдаться.

Issue: #248 (AURORA-1877, эпик AURORA-1876, Критичный приоритет)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-30 17:18:11 +04:00

39 lines
971 B
JavaScript

import { getTokenExp } from './tokenRefresh.js';
export const saveAuthTokens = (data) => {
const { access_token, refresh_token, expires_in } = data;
// TODO: раскомментировать, когда поправят expires_in на бэке
// const expiresAt = Date.now() + expires_in * 1000
localStorage.setItem('access_token', access_token);
localStorage.setItem('refresh_token', refresh_token);
// localStorage.setItem('expires_at', expiresAt.toString())
};
export const getAuthToken = () => {
const access_token = localStorage.getItem('access_token');
return access_token;
};
export const isTokenExpired = () => {
const token = getAuthToken();
if (!token) {
return true;
}
const exp = getTokenExp(token);
if (exp === null) {
return true;
}
const now = Math.floor(Date.now() / 1000);
return exp < now;
};
export const clearAuthTokens = () => {
localStorage.removeItem('access_token');
localStorage.removeItem('refresh_token');
};