81 lines
2.7 KiB
TypeScript
81 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 };
|
||
|
|
};
|