elearning/frontend_management/composables/useAuthFetch.ts

80 lines
2.7 KiB
TypeScript

import { authService } from '~/services/auth.service';
/**
* Custom fetch composable that handles automatic token refresh
*
* Flow:
* 1. Get token from cookie
* 2. Make API request with token
* 3. If 401 error (token expired):
* - Try to refresh token using refreshToken
* - If refresh successful, retry original request
* - If refresh fails, redirect to login
*/
export const useAuthFetch = () => {
const config = useRuntimeConfig();
const router = useRouter();
const authFetch = async <T>(
url: string,
options: {
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
body?: any;
headers?: Record<string, string>;
} = {}
): Promise<T> => {
const tokenCookie = useCookie('token');
const refreshTokenCookie = useCookie('refreshToken');
const makeRequest = async (token: string | null) => {
return await $fetch<T>(url, {
baseURL: config.public.apiBaseUrl as string,
method: options.method || 'GET',
headers: {
...options.headers,
...(token ? { Authorization: `Bearer ${token}` } : {})
},
body: options.body
});
};
try {
// Try request with current token
return await makeRequest(tokenCookie.value ?? null);
} catch (error: any) {
// If 401 and we have refresh token, try to refresh
if (error.response?.status === 401 && refreshTokenCookie.value) {
console.log('Token expired, attempting refresh...');
try {
// Refresh token
const newTokens = await authService.refreshToken(refreshTokenCookie.value);
console.log('Token refreshed successfully');
// Update cookies
tokenCookie.value = newTokens.token;
refreshTokenCookie.value = newTokens.refreshToken;
// Retry original request with new token
return await makeRequest(newTokens.token);
} catch (refreshError) {
console.error('Token refresh failed, redirecting to login');
// Clear cookies
tokenCookie.value = null;
refreshTokenCookie.value = null;
useCookie('user').value = null;
// Redirect to login
router.push('/login');
throw new Error('Session expired. Please login again.');
}
}
// Other errors, just throw
throw error;
}
};
return { authFetch };
};