Adds access token refreshing.
This commit is contained in:
@@ -19,6 +19,34 @@ export function useClient() {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
'auth',
|
||||
() => {
|
||||
@@ -22,7 +33,7 @@ export const useAuthStore = defineStore(
|
||||
const accessToken = useSessionStorage('auth-accessToken', undefined)
|
||||
const refreshToken = useSessionStorage('auth-refreshToken', undefined)
|
||||
|
||||
const isAuthenticated = computed(() => !!accessToken.value)
|
||||
const isAuthenticated = computed(() => !!accessToken.value && !isTokenExpired(accessToken.value))
|
||||
|
||||
const userId = computed(() => {
|
||||
const claims = getClaimsFromToken(accessToken.value)
|
||||
@@ -96,20 +107,33 @@ export const useAuthStore = defineStore(
|
||||
}
|
||||
|
||||
async function refresh() {
|
||||
if (!refreshToken.value) {
|
||||
throw new Error('No refresh token available');
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await clientApi.post(
|
||||
'api/users/refresh',
|
||||
{
|
||||
refreshToken: refreshToken
|
||||
refreshToken: refreshToken.value
|
||||
});
|
||||
|
||||
updateTokens({
|
||||
accessToken: response.accessToken,
|
||||
refreshToken: refreshToken
|
||||
})
|
||||
accessToken: response.data.accessToken,
|
||||
refreshToken: response.data.refreshToken
|
||||
});
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
cleanTokens()
|
||||
console.error('Token refresh failed:', error);
|
||||
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,
|
||||
loginWithGoogle,
|
||||
loginWithFacebook,
|
||||
logout
|
||||
logout,
|
||||
refresh,
|
||||
ensureValidToken
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user