84 lines
2.6 KiB
TypeScript
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;
|