// 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( 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( 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 ( {children} ); };