import { logger } from '../services/logging'; export interface AuthTokens { accessToken: string; refreshToken?: string | null; } const TOKEN_STORAGE_KEY = 'meds_auth_tokens'; let memoryTokens: AuthTokens | null = null; function getStorage(): Storage | null { if (typeof window === 'undefined') { return null; } try { return window.localStorage; } catch (error) { // LocalStorage may be unavailable (e.g. Safari private mode); gracefully degrade. logger.warn('Token storage fallback to memory', 'AUTH_TOKENS', error); return null; } } function persist(tokens: AuthTokens | null): void { const storage = getStorage(); if (!storage) { memoryTokens = tokens; return; } if (!tokens) { storage.removeItem(TOKEN_STORAGE_KEY); memoryTokens = null; return; } const payload: AuthTokens = { accessToken: tokens.accessToken, refreshToken: tokens.refreshToken ?? null, }; storage.setItem(TOKEN_STORAGE_KEY, JSON.stringify(payload)); memoryTokens = payload; } function readTokens(): AuthTokens | null { const storage = getStorage(); if (!storage) { return memoryTokens; } try { const raw = storage.getItem(TOKEN_STORAGE_KEY); if (!raw) { memoryTokens = null; return null; } const parsed = JSON.parse(raw) as AuthTokens; memoryTokens = parsed; return parsed; } catch (error) { logger.warn( 'Failed to parse stored tokens, clearing cache', 'AUTH_TOKENS', error ); storage.removeItem(TOKEN_STORAGE_KEY); memoryTokens = null; return null; } } export const tokenStorage = { save(tokens: AuthTokens): void { if (!tokens || !tokens.accessToken) { throw new Error('Token payload must include an access token'); } persist(tokens); }, getTokens(): AuthTokens | null { return readTokens(); }, getAccessToken(): string | null { const tokens = readTokens(); return tokens?.accessToken ?? null; }, clear(): void { persist(null); }, }; export default tokenStorage;