Adds access token refreshing.

This commit is contained in:
2025-04-17 01:43:28 -04:00
parent 9d2a00a928
commit c19e2eb493
2 changed files with 62 additions and 8 deletions

View File

@@ -19,6 +19,34 @@ export function useClient() {
api.interceptors.request.use(requestInterceptor); api.interceptors.request.use(requestInterceptor);
// Add response interceptor for token refresh
api.interceptors.response.use(
(response) => response,
async (error) => {
const originalRequest = error.config;
// If error is 401 and we haven't tried to refresh the token yet
if (error.response?.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
try {
// Attempt to refresh the token
await authStore.refresh();
// Retry the original request with the new token
originalRequest.headers["Authorization"] = `Bearer ${authStore.accessToken}`;
return api(originalRequest);
} catch (refreshError) {
// If refresh fails, logout the user
await authStore.logout();
return Promise.reject(refreshError);
}
}
return Promise.reject(error);
}
);
return api; return api;
} }

View File

@@ -13,6 +13,17 @@ function getClaimsFromToken(token) {
} }
} }
function isTokenExpired(token) {
if (!token) return true;
const claims = getClaimsFromToken(token);
if (!claims) return true;
// Check if token will expire in the next 5 minutes
const expirationTime = claims.exp * 1000; // Convert to milliseconds
const currentTime = Date.now();
return currentTime >= expirationTime - 5 * 60 * 1000; // 5 minutes before expiration
}
export const useAuthStore = defineStore( export const useAuthStore = defineStore(
'auth', 'auth',
() => { () => {
@@ -22,7 +33,7 @@ export const useAuthStore = defineStore(
const accessToken = useSessionStorage('auth-accessToken', undefined) const accessToken = useSessionStorage('auth-accessToken', undefined)
const refreshToken = useSessionStorage('auth-refreshToken', undefined) const refreshToken = useSessionStorage('auth-refreshToken', undefined)
const isAuthenticated = computed(() => !!accessToken.value) const isAuthenticated = computed(() => !!accessToken.value && !isTokenExpired(accessToken.value))
const userId = computed(() => { const userId = computed(() => {
const claims = getClaimsFromToken(accessToken.value) const claims = getClaimsFromToken(accessToken.value)
@@ -96,20 +107,33 @@ export const useAuthStore = defineStore(
} }
async function refresh() { async function refresh() {
if (!refreshToken.value) {
throw new Error('No refresh token available');
}
try { try {
const response = await clientApi.post( const response = await clientApi.post(
'api/users/refresh', 'api/users/refresh',
{ {
refreshToken: refreshToken refreshToken: refreshToken.value
}); });
updateTokens({ updateTokens({
accessToken: response.accessToken, accessToken: response.data.accessToken,
refreshToken: refreshToken refreshToken: response.data.refreshToken
}) });
return true;
} catch (error) { } catch (error) {
console.error(error) console.error('Token refresh failed:', error);
cleanTokens() cleanTokens();
throw error;
}
}
// Function to check if token needs refresh
async function ensureValidToken() {
if (isTokenExpired(accessToken.value)) {
await refresh();
} }
} }
@@ -121,6 +145,8 @@ export const useAuthStore = defineStore(
login, login,
loginWithGoogle, loginWithGoogle,
loginWithFacebook, loginWithFacebook,
logout logout,
refresh,
ensureValidToken
} }
}) })