105 lines
3.1 KiB
TypeScript
105 lines
3.1 KiB
TypeScript
import type { H3Event } from "h3";
|
|
import { jwtDecode } from "jwt-decode";
|
|
import type { User } from "#auth-utils";
|
|
import type { AuthUserFragment, AuthLoginMutationResult } from "#graphql/operations";
|
|
import { AuthRefreshTokenDocument } from "#graphql/operations";
|
|
import type { ResultOf } from "#graphql/registry";
|
|
|
|
// Handle login result and store user session
|
|
export async function handleLogin(event: H3Event, loginResult: AuthLoginMutationResult) {
|
|
if (!loginResult?.login) {
|
|
return false;
|
|
}
|
|
const { user, authToken, refreshToken } = loginResult.login;
|
|
if (!user || !authToken || !refreshToken) {
|
|
return false;
|
|
}
|
|
await setUserSession(event, {
|
|
user: getAuthUser(user),
|
|
secure: {
|
|
authToken,
|
|
refreshToken,
|
|
},
|
|
loggedInAt: new Date().toISOString(),
|
|
});
|
|
return true;
|
|
}
|
|
|
|
// Handle user logout by clearing session
|
|
export async function handleLogout(event: H3Event) {
|
|
await clearUserSession(event);
|
|
return true;
|
|
}
|
|
|
|
// Convert AuthUserFragment to nuxt-auth-utils User
|
|
function getAuthUser(user: AuthUserFragment): User {
|
|
return {
|
|
id: Number(user.id),
|
|
email: user.email!,
|
|
roles: extractNodes(user.roles).map(({ name }) => name!) || [],
|
|
};
|
|
}
|
|
|
|
// 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 executeGraphQLHTTP<ResultOf<"AuthRefreshToken">>(
|
|
{
|
|
query: AuthRefreshTokenDocument,
|
|
variables: { refreshToken },
|
|
},
|
|
{ endpoint },
|
|
);
|
|
return data?.refreshToken?.authToken || undefined;
|
|
})();
|
|
|
|
refreshTokenPromises.set(refreshToken, refreshPromise);
|
|
|
|
return refreshPromise.finally(() => {
|
|
const current = refreshTokenPromises.get(refreshToken);
|
|
if (current === refreshPromise) {
|
|
refreshTokenPromises.delete(refreshToken);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Get auth token from user session (refresh if needed)
|
|
export async function getAuthToken(event: H3Event): Promise<string | undefined> {
|
|
// Retrieve user session, return if none
|
|
const session = await getUserSession(event);
|
|
if (!session.secure) {
|
|
return;
|
|
}
|
|
|
|
// Extract tokens and check expiration
|
|
const { authToken, refreshToken } = session.secure;
|
|
const decoded = jwtDecode<{ exp: number }>(authToken);
|
|
const isExpired = decoded.exp * 1000 < Date.now();
|
|
if (isExpired) {
|
|
try {
|
|
const newAuthToken = await refreshAuthToken(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;
|
|
}
|