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

@@ -0,0 +1,29 @@
fragment AuthUser on User {
id
email @nonNull
roles @nonNull {
nodes {
name @nonNull
}
}
}
fragment AuthPayload on LoginPayload {
authToken
refreshToken
user {
...AuthUser
}
}
mutation AuthLogin($username: String!, $password: String!) {
login(input: { provider: PASSWORD, credentials: { username: $username, password: $password } }) {
...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 isExpired = decoded.exp * 1000 < Date.now();
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;
}
// 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);
}
});
}