Files
papercrate/frontend/src/hooks/documents/useAuthManager.ts
T
2025-11-23 00:11:18 +01:00

84 lines
2.6 KiB
TypeScript

import { useCallback, useEffect, useRef } from 'react';
import type { MutableRefObject } from 'react';
import { clearAuthToken, logoutSession, refreshSession, setAuthToken } from '../../lib/apiClient';
type AppStatus = string;
type AppDispatch = (action: { type: string; [key: string]: unknown }) => void;
type SetStatusMessage = (message: string, variant?: string) => void;
interface UseAuthManagerArgs {
token?: string | null;
appStatus: AppStatus;
appDispatch: AppDispatch;
setStatusMessage: SetStatusMessage;
}
interface UseAuthManagerResult {
tokenRef: MutableRefObject<string | null>;
refreshAccessToken: () => Promise<string>;
handleLogout: () => Promise<void>;
}
const useAuthManager = ({
token,
appStatus,
appDispatch,
setStatusMessage,
}: UseAuthManagerArgs): UseAuthManagerResult => {
const tokenRef = useRef<string | null>(token);
const initialRefreshAttemptedRef = useRef(Boolean(token));
const refreshAccessToken = useCallback(async (): Promise<string> => {
console.log('[Auth] Attempting to refresh access token…');
appDispatch({ type: 'TOKEN_REFRESH_START' });
try {
const data = await refreshSession();
if (data?.access_token) {
setAuthToken(data.access_token);
appDispatch({
type: 'TOKEN_REFRESH_SUCCESS',
token: data.access_token,
tenant: data.tenant || null,
});
console.log('[Auth] Access token refreshed at', new Date().toISOString());
return data.access_token;
}
throw new Error('Missing access token in refresh response');
} catch (error) {
console.warn('[Auth] Failed to refresh access token', error);
appDispatch({ type: 'TOKEN_REFRESH_FAILURE', error: (error as Error)?.message || null });
throw error;
}
}, [appDispatch]);
useEffect(() => {
tokenRef.current = token;
}, [token]);
useEffect(() => {
if (!token && !initialRefreshAttemptedRef.current && appStatus === 'logged-out') {
initialRefreshAttemptedRef.current = true;
console.log('[Auth] Attempting refresh at startup');
refreshAccessToken().catch(() => {});
}
}, [token, appStatus, refreshAccessToken]);
const handleLogout = useCallback(async () => {
try {
await logoutSession();
} catch (error) {
console.warn('[Auth] Failed to revoke refresh token during logout', error);
} finally {
clearAuthToken();
appDispatch({ type: 'LOGOUT' });
setStatusMessage('Logged out.', 'info');
}
}, [appDispatch, setStatusMessage]);
return { tokenRef, refreshAccessToken, handleLogout };
};
export default useAuthManager;