feat: Authentication token refresh logic

This commit is contained in:
2026-03-26 15:46:53 -04:00
parent 30c7b8b0b5
commit d758f53176
4 changed files with 58 additions and 6 deletions

View File

@@ -1,4 +1,4 @@
export default defineGraphQLContext(async (event) => { export default defineGraphQLContext(async (event) => {
const wpAuthToken = await getAuthToken(event); const authToken = await getAuthToken(event);
return { wpAuthToken }; return { authToken };
}); });

View File

@@ -2,10 +2,10 @@ import { defu } from "defu";
export default defineRemoteExecutorHooks({ export default defineRemoteExecutorHooks({
onRequest(request, context) { onRequest(request, context) {
// Attach the Authorization header if a wpAuthToken is present in the context // Attach the Authorization header if an authToken is present in the context
if (context?.wpAuthToken) { if (context?.authToken) {
request.extensions = defu(request.extensions, { request.extensions = defu(request.extensions, {
headers: { Authorization: `Bearer ${context.wpAuthToken}` }, headers: { Authorization: `Bearer ${context.authToken}` },
}); });
} }
}, },

View File

@@ -21,3 +21,9 @@ mutation AuthLogin($username: String!, $password: String!) {
...AuthPayload ...AuthPayload
} }
} }
mutation AuthRefreshToken($refreshToken: String!) {
refreshToken(input: { refreshToken: $refreshToken }) {
authToken
}
}

View File

@@ -67,8 +67,54 @@ export async function getAuthToken(event: H3Event) {
const decoded = jwtDecode<{ exp: number }>(session.secure.authToken); const decoded = jwtDecode<{ exp: number }>(session.secure.authToken);
const isExpired = decoded.exp * 1000 < Date.now(); const isExpired = decoded.exp * 1000 < Date.now();
if (isExpired) { if (isExpired) {
// TOOD: Refresh token logic try {
const newAuthToken = await refreshAuthToken(session.secure.refreshToken);
if (!newAuthToken) {
throw new Error("Impossible de rafraîchir le jeton d'authentification.");
}
session.secure.authToken = newAuthToken;
await setUserSession(event, session);
} catch {
await clearUserSession(event);
return;
}
} }
return session.secure.authToken; return session.secure.authToken;
} }
// Track in-flight refreshAuthToken calls to prevent duplicate requests
const refreshTokenPromises = new Map<string, Promise<string | undefined>>();
// Refresh auth token by calling remote GraphQL endpoint directly
export async function refreshAuthToken(refreshToken: string): Promise<string | undefined> {
// Return existing in-flight promise if available
const inFlight = refreshTokenPromises.get(refreshToken);
if (inFlight) {
return inFlight;
}
const refreshPromise = (async () => {
const { wpUrl } = useRuntimeConfig();
const endpoint = `${wpUrl}/graphql`;
const { data } = await executeHttpOperation(
{
operationName: "AuthRefreshToken",
variables: { refreshToken },
},
{ endpoint, headers: { Authorization: null } },
);
return data?.refreshToken?.authToken || undefined;
})();
refreshTokenPromises.set(refreshToken, refreshPromise);
return refreshPromise.finally(() => {
const current = refreshTokenPromises.get(refreshToken);
if (current === refreshPromise) {
refreshTokenPromises.delete(refreshToken);
}
});
}