Files
magistr/frontend/auth-session.js
2026-07-19 14:40:43 +03:00

120 lines
3.1 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const LEGACY_AUTH_KEYS = ['token', 'role', 'departmentId', 'userId'];
let accessToken = null;
let authState = null;
let refreshPromise = null;
purgeLegacyAuthStorage();
export function getAccessToken() {
return accessToken;
}
export function getAuthState() {
return authState ? { ...authState } : null;
}
export function storeAuthState(data) {
if (!data?.token || !data?.role) {
clearAuthState();
return null;
}
accessToken = data.token;
authState = {
role: data.role,
departmentId: normalizeOptionalId(data.departmentId),
userId: normalizeOptionalId(data.userId),
redirect: typeof data.redirect === 'string' ? data.redirect : '/'
};
return getAuthState();
}
export function clearAuthState() {
accessToken = null;
authState = null;
}
export async function restoreSession() {
if (accessToken && authState) {
return getAuthState();
}
const refreshed = await refreshAccessToken();
return refreshed ? getAuthState() : null;
}
export async function refreshAccessToken() {
if (refreshPromise) {
return refreshPromise;
}
refreshPromise = (async () => {
let response;
try {
response = await fetch('/api/auth/refresh', {
method: 'POST',
credentials: 'same-origin'
});
} catch (error) {
clearAuthState();
return false;
}
const data = await response.json().catch(() => null);
if (!response.ok || !storeAuthState(data)) {
clearAuthState();
return false;
}
return true;
})().finally(() => {
refreshPromise = null;
});
return refreshPromise;
}
export async function fetchWithAuth(endpoint, options = {}, retryOnUnauthorized = true) {
const headers = new Headers(options.headers || {});
if (accessToken) {
headers.set('Authorization', `Bearer ${accessToken}`);
}
const response = await fetch(endpoint, {
...options,
headers,
credentials: 'same-origin'
});
if (response.status === 401 && retryOnUnauthorized && await refreshAccessToken()) {
return fetchWithAuth(endpoint, options, false);
}
return response;
}
export async function logoutSession() {
try {
await fetch('/api/auth/logout', {
method: 'POST',
credentials: 'same-origin'
});
} catch (error) {
console.warn('Не удалось завершить серверную сессию');
} finally {
clearAuthState();
}
}
function purgeLegacyAuthStorage() {
try {
LEGACY_AUTH_KEYS.forEach(key => localStorage.removeItem(key));
LEGACY_AUTH_KEYS.forEach(key => sessionStorage.removeItem(key));
} catch (error) {
console.warn('Не удалось очистить устаревшие данные авторизации в браузере');
}
}
function normalizeOptionalId(value) {
return value === undefined || value === null || value === '' ? null : String(value);
}