kaboot/hooks/useAuthenticatedFetch.ts

97 lines
2.7 KiB
TypeScript

import { useAuth } from 'react-oidc-context';
import { useCallback } from 'react';
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:3001';
export const useAuthenticatedFetch = () => {
const auth = useAuth();
const isTokenExpired = useCallback(() => {
if (!auth.user?.expires_at) return true;
const expiresAt = auth.user.expires_at * 1000;
const now = Date.now();
const bufferMs = 60 * 1000;
return now >= expiresAt - bufferMs;
}, [auth.user?.expires_at]);
const ensureValidToken = useCallback(async (): Promise<string> => {
if (!auth.user?.access_token) {
throw new Error('Not authenticated');
}
if (isTokenExpired()) {
try {
const user = await auth.signinSilent();
if (user?.access_token) {
return user.access_token;
}
} catch {
auth.signinRedirect();
throw new Error('Session expired, redirecting to login');
}
}
return auth.user.access_token;
}, [auth, isTokenExpired]);
const authFetch = useCallback(
async (path: string, options: RequestInit = {}): Promise<Response> => {
if (!navigator.onLine) {
throw new Error('You appear to be offline. Please check your connection.');
}
const token = await ensureValidToken();
const url = path.startsWith('http') ? path : `${API_URL}${path}`;
let response: Response;
try {
response = await fetch(url, {
...options,
headers: {
...options.headers,
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
});
} catch (err) {
if (!navigator.onLine) {
throw new Error('You appear to be offline. Please check your connection.');
}
throw new Error('Network error. Please try again.');
}
if (response.status === 401) {
try {
const user = await auth.signinSilent();
if (user?.access_token) {
return fetch(url, {
...options,
headers: {
...options.headers,
Authorization: `Bearer ${user.access_token}`,
'Content-Type': 'application/json',
},
});
}
} catch {
auth.signinRedirect();
}
throw new Error('Session expired, redirecting to login');
}
if (response.status >= 500) {
throw new Error('Server error. Please try again later.');
}
return response;
},
[auth, ensureValidToken]
);
return {
authFetch,
isAuthenticated: auth.isAuthenticated,
isLoading: auth.isLoading,
user: auth.user,
};
};