Files
Account/src/providers/AuthProvider.tsx
2025-08-16 15:42:58 +03:30

120 lines
3.3 KiB
TypeScript

// useAuth.tsx
import { AuthContext } from '@/contexts/AuthContext';
import type { GenerateTokenResponse } from '@/features/authorization/api/identityAPI';
import axios from 'axios';
import { useEffect, useState, type ReactNode } from 'react';
export const ACCESS_TOKEN_KEY: 'access_token' = 'access_token' as const;
export const REFRESH_TOKEN_KEY: 'refresh_token' = 'refresh_token' as const;
export const EXPIRES_IN_KEY: 'expires_in' = 'expires_in' as const;
let inMemoryToken: string | null = null;
let expiresAt = 0;
export const AuthProvider = ({ children }: { children: ReactNode }) => {
const [accessToken, setAccessToken] = useState<string | null>(
sessionStorage.getItem(ACCESS_TOKEN_KEY),
);
// Initialize from sessionStorage (page reload)
useEffect(() => {
const token = sessionStorage.getItem(ACCESS_TOKEN_KEY);
const exp = Number(sessionStorage.getItem(EXPIRES_IN_KEY) || 0);
if (token && Date.now() < exp) {
inMemoryToken = token;
expiresAt = exp;
setAccessToken(token);
}
}, []);
// Background refresh
useEffect(() => {
const handleRefreshTokenIfNeeded = async () => {
if (!inMemoryToken) return;
if (Date.now() < expiresAt - 60_000) return; // still valid (buffer)
await refreshAccessToken();
setAccessToken(inMemoryToken);
};
handleRefreshTokenIfNeeded();
const interval = setInterval(async () => {
await handleRefreshTokenIfNeeded();
}, 30_000);
return () => clearInterval(interval);
}, []);
function login(tokens: {
access_token: string;
refresh_token: string;
expires_in: number;
}) {
inMemoryToken = tokens.access_token;
expiresAt = Date.now() + tokens.expires_in * 1000;
sessionStorage.setItem(ACCESS_TOKEN_KEY, tokens.access_token);
sessionStorage.setItem(REFRESH_TOKEN_KEY, tokens.refresh_token);
sessionStorage.setItem(EXPIRES_IN_KEY, String(expiresAt));
setAccessToken(tokens.access_token);
}
function logout() {
inMemoryToken = null;
expiresAt = 0;
sessionStorage.clear();
setAccessToken(null);
}
function getToken() {
return inMemoryToken;
}
async function refreshAccessToken() {
const refreshToken = sessionStorage.getItem(REFRESH_TOKEN_KEY);
if (!refreshToken) {
logout();
return;
}
try {
const result = await axios.post<GenerateTokenResponse>(
import.meta.env.VITE_IDENTITY_URL,
new URLSearchParams({
grant_type: 'refresh_token',
refresh_token: refreshToken,
client_id: import.meta.env.VITE_IDENTITY_CLIENT_ID, // from your token payload
}),
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
},
);
if (result.data.access_token) {
inMemoryToken = result.data.access_token;
expiresAt = Date.now() + result.data.expires_in * 1000;
sessionStorage.setItem(ACCESS_TOKEN_KEY, inMemoryToken as string);
sessionStorage.setItem(EXPIRES_IN_KEY, String(expiresAt));
setAccessToken(inMemoryToken);
} else {
logout();
}
} catch {
logout();
}
}
return (
<AuthContext.Provider value={{ accessToken, getToken, login, logout }}>
{children}
</AuthContext.Provider>
);
};