Refactor GraphQL API routes and remove unused components; enhance error handling and context management

This commit is contained in:
Alexandre Bove 2025-07-30 12:29:13 +02:00
parent f1f1b45c91
commit 7d0a6e5a74
16 changed files with 66 additions and 314 deletions

View File

@ -1,3 +1,5 @@
import { yoga } from "@/graphql"; import { yoga } from "@/graphql";
export { yoga as GET, yoga as POST, yoga as OPTIONS }; export const GET = (request: Request) => yoga.handleRequest(request, { request, db: {} });
export const POST = (request: Request) => yoga.handleRequest(request, { request, db: {} });
export const OPTIONS = (request: Request) => yoga.handleRequest(request, { request, db: {} });

View File

@ -1,114 +0,0 @@
"use client";
import { useSearchParams } from "next/navigation";
import { useEffect, useState } from "react";
export default function CallbackPage() {
const [status, setStatus] = useState<"loading" | "success" | "error">("loading");
const [message, setMessage] = useState("");
const searchParams = useSearchParams();
useEffect(() => {
const handleCallback = async () => {
try {
const code = searchParams.get("code");
const state = searchParams.get("state");
const error = searchParams.get("error");
if (error) {
setStatus("error");
setMessage(`Erreur d'authentification: ${error}`);
return;
}
if (!code || !state) {
setStatus("error");
setMessage("Paramètres de callback manquants");
return;
}
// Décoder le state pour récupérer les informations du type d'authentification
const stateData = JSON.parse(decodeURIComponent(state));
const { type, connector } = stateData;
// Ici vous pouvez traiter le code d'autorisation pour obtenir les tokens
// et créer ou connecter l'utilisateur avec votre API
setStatus("success");
setMessage(
`Authentification ${type === "login" ? "connexion" : "inscription"} réussie avec le connecteur ${connector}`
);
// Rediriger vers la page appropriée après un délai
setTimeout(() => {
if (type === "login") {
window.location.href = "/profile";
} else {
window.location.href = "/profile";
}
}, 2000);
} catch (err) {
console.error("Erreur lors du traitement du callback:", err);
setStatus("error");
setMessage("Erreur lors du traitement de l'authentification");
}
};
handleCallback();
}, [searchParams]);
return (
<div className="flex min-h-screen items-center justify-center bg-gray-50 px-4 py-12 sm:px-6 lg:px-8">
<div className="w-full max-w-md space-y-8">
<div className="rounded-lg bg-white p-8 text-center shadow-lg">
{status === "loading" && (
<>
<div className="mx-auto h-12 w-12 animate-spin rounded-full border-4 border-blue-500 border-t-transparent"></div>
<h2 className="mt-4 text-xl font-semibold text-gray-900">Authentification en cours...</h2>
<p className="mt-2 text-gray-600">Veuillez patienter pendant que nous traitons votre authentification.</p>
</>
)}
{status === "success" && (
<>
<div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-green-100">
<svg className="h-6 w-6 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M5 13l4 4L19 7"></path>
</svg>
</div>
<h2 className="mt-4 text-xl font-semibold text-green-900">Authentification réussie !</h2>
<p className="mt-2 text-gray-600">{message}</p>
<p className="mt-2 text-sm text-gray-500">Redirection en cours...</p>
</>
)}
{status === "error" && (
<>
<div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-red-100">
<svg className="h-6 w-6 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</div>
<h2 className="mt-4 text-xl font-semibold text-red-900">Erreur d&apos;authentification</h2>
<p className="mt-2 text-gray-600">{message}</p>
<div className="mt-6 space-y-3">
<a
href="/login"
className="inline-flex w-full justify-center rounded-md border border-transparent bg-blue-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-blue-700 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 focus:outline-none"
>
Retour à la connexion
</a>
<a
href="/signup"
className="inline-flex w-full justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 focus:outline-none"
>
Retour à l&apos;inscription
</a>
</div>
</>
)}
</div>
</div>
</div>
);
}

View File

@ -68,19 +68,6 @@ const CREATE_CONNECTOR_MUTATION = gql`
} }
`; `;
const UPDATE_CONNECTOR_MUTATION = gql`
mutation UpdateConnector($input: UpdateConnectorInput!) {
updateConnector(input: $input) {
success
message
connector {
id
enabled
}
}
}
`;
const DELETE_CONNECTOR_MUTATION = gql` const DELETE_CONNECTOR_MUTATION = gql`
mutation DeleteConnector($id: ID!) { mutation DeleteConnector($id: ID!) {
deleteConnector(id: $id) { deleteConnector(id: $id) {

View File

@ -23,7 +23,7 @@ export default function RolesPage() {
// Fetch roles // Fetch roles
useEffect(() => { useEffect(() => {
client client
.request(gql` .request<{ roles: Role[] }>(gql`
{ {
roles { roles {
id id
@ -37,7 +37,7 @@ export default function RolesPage() {
} }
} }
`) `)
.then((data: any) => { .then(data => {
setRoles(data.roles); setRoles(data.roles);
setLoading(false); setLoading(false);
}) })

View File

@ -70,11 +70,6 @@ export default function PermissionsManager({
} }
} }
`; `;
console.log("[DEBUG] createResourceScope called", {
resourceId: selectedResource,
name: form.name,
description: form.description,
});
const variables = { resourceId: selectedResource, name: form.name, description: form.description || undefined }; const variables = { resourceId: selectedResource, name: form.name, description: form.description || undefined };
const data: { createResourceScope: Permission } = await client.request(mutation, variables); const data: { createResourceScope: Permission } = await client.request(mutation, variables);
setScopes([...scopes, data.createResourceScope]); setScopes([...scopes, data.createResourceScope]);

View File

@ -1,6 +1,5 @@
import setAuth from "@/graphql/context/features/auth"; import setAuth from "@/graphql/context/features/auth";
import initGuard from "@/graphql/context/features/guard"; import initGuard from "@/graphql/context/features/guard";
import { patContextCache } from "@/graphql/context/features/patCache";
import { GraphQLContext } from "@/graphql/context/types"; import { GraphQLContext } from "@/graphql/context/types";
import { YogaInitialContext } from "graphql-yoga"; import { YogaInitialContext } from "graphql-yoga";
@ -13,7 +12,6 @@ export async function context({ request }: YogaInitialContext): Promise<GraphQLC
request, request,
...auth, ...auth,
guard, guard,
patCache: patContextCache,
}; };
return context; return context;

View File

@ -1,5 +1,4 @@
import { AuthTypes } from "@/graphql/context"; import { AuthTypes } from "@/graphql/context";
import { patContextCache } from "@/graphql/context/features/patCache";
import { listResources, Resource } from "@/graphql/features/Resource/resourceService"; import { listResources, Resource } from "@/graphql/features/Resource/resourceService";
import { getUserRoles, Role } from "@/graphql/features/Role/roleService"; import { getUserRoles, Role } from "@/graphql/features/Role/roleService";
import { validatePATWithLogtoAPI } from "@/graphql/features/User/resolver"; import { validatePATWithLogtoAPI } from "@/graphql/features/User/resolver";
@ -69,7 +68,7 @@ const setAuth = async (
if (token.includes(".") && token.split(".").length === 3) { if (token.includes(".") && token.split(".").length === 3) {
// C'est un JWT // C'est un JWT
const decoded = decodeJWT(token); const decoded = decodeJWT(token);
userId = decoded?.sub as string | undefined; userId = decoded?.sub;
if (userId) { if (userId) {
user = { user = {
id: userId, id: userId,
@ -79,27 +78,23 @@ const setAuth = async (
isActive: typeof decoded?.isActive === "boolean" ? decoded.isActive : undefined, isActive: typeof decoded?.isActive === "boolean" ? decoded.isActive : undefined,
}; };
} }
// Log JWT
console.error("[setAuth] JWT decoded:", decoded);
} else if (token.startsWith("pat_")) { } else if (token.startsWith("pat_")) {
// PAT : tenter de récupérer l'userId via le cache
userId = patContextCache.getUserId(token) || undefined;
if (!userId) {
// Si non trouvé dans le cache, tente une validation via Logto
try { try {
userId = await validatePATWithLogtoAPI(token, { patCache: patContextCache, db: {}, request: req }); let requestObj: Request;
if (userId) { if (req instanceof Request) {
patContextCache.register(token, userId, 24); requestObj = req;
} else {
// Create a minimal Request object if possible, or throw an error
throw new Error("PAT validation requires a Request object.");
} }
} catch (e) { userId = (await validatePATWithLogtoAPI(token, { db: {}, request: requestObj })) || undefined;
console.error("[setAuth] PAT non reconnu via Logto:", token, e);
}
}
console.error("[setAuth] PAT lookup:", { token, userId });
if (userId) { if (userId) {
user = { id: userId }; user = { id: userId };
} else { } else {
console.error("[setAuth] PAT non reconnu dans le cache ni via Logto, token:", token); console.error("[setAuth] PAT non reconnu via Logto", { token });
}
} catch (e) {
console.error("[setAuth] Erreur lors de la validation du PAT via Logto:", token, e);
} }
} }
@ -112,7 +107,6 @@ const setAuth = async (
accessToken: token, accessToken: token,
request: req as Request, request: req as Request,
db: {}, db: {},
patCache: patContextCache,
}; };
roles = await getUserRoles(userId, minimalContext); roles = await getUserRoles(userId, minimalContext);
permissions = roles.flatMap(r => permissions = roles.flatMap(r =>

View File

@ -1,82 +0,0 @@
// Cache pour les associations PAT -> userId intégré au contexte GraphQL
export interface PATCacheEntry {
userId: string;
expiry: number;
}
class PATContextCache {
private cache: Map<string, PATCacheEntry> = new Map();
// Enregistrer une association PAT -> userId
register(pat: string, userId: string, ttlHours: number = 24): void {
this.cache.set(pat, {
userId: userId,
expiry: Date.now() + ttlHours * 60 * 60 * 1000,
});
}
// Récupérer l'userId depuis un PAT
getUserId(pat: string): string | null {
const entry = this.cache.get(pat);
if (entry && Date.now() < entry.expiry) {
return entry.userId;
}
// Supprimer l'entrée expirée
if (entry) {
this.cache.delete(pat);
}
return null;
}
// Supprimer une entrée du cache
remove(pat: string): void {
this.cache.delete(pat);
}
// Nettoyer les entrées expirées
cleanup(): void {
const now = Date.now();
for (const [pat, entry] of this.cache.entries()) {
if (now >= entry.expiry) {
this.cache.delete(pat);
}
}
}
// Obtenir les statistiques du cache
getStats(): { size: number; entries: number } {
this.cleanup(); // Nettoyer avant de retourner les stats
return {
size: this.cache.size,
entries: this.cache.size,
};
}
// Vérifier si un PAT existe dans le cache
has(pat: string): boolean {
const entry = this.cache.get(pat);
if (entry && Date.now() < entry.expiry) {
return true;
}
// Supprimer l'entrée expirée
if (entry) {
this.cache.delete(pat);
}
return false;
}
}
// Instance singleton du cache PAT
export const patContextCache = new PATContextCache();
// Nettoyer le cache toutes les heures
setInterval(
() => {
patContextCache.cleanup();
},
60 * 60 * 1000
); // 1 heure

View File

@ -1,11 +1,9 @@
import type { Resource } from "@/graphql/features/Resource/resourceService"; import type { Resource } from "@/graphql/features/Resource/resourceService";
import type { Role } from "@/graphql/features/Role/roleService"; import type { Role } from "@/graphql/features/Role/roleService";
export interface DB { export type DB = object;
// Define your database types here
}
export interface Guard {} export type Guard = object;
export interface GuardFunction { export interface GuardFunction {
(auth?: AuthTypes): Guard; (auth?: AuthTypes): Guard;
@ -50,5 +48,4 @@ export interface GraphQLContext {
accessToken?: string; accessToken?: string;
personalAccessToken?: string; personalAccessToken?: string;
userId?: string; userId?: string;
patCache: PATCache; // Cache PAT intégré au contexte
} }

View File

@ -192,12 +192,6 @@ export const organizationResolvers = {
context: GraphQLContext context: GraphQLContext
): Promise<OrganizationsListResponse> { ): Promise<OrganizationsListResponse> {
try { try {
// DEBUG: log du contexte d'authentification
console.log("[myOrganizations] context.user:", context.user);
console.log("[myOrganizations] context.userId:", context.userId);
if (context.request && typeof context.request.headers?.get === "function") {
console.log("[myOrganizations] Authorization header:", context.request.headers.get("Authorization"));
}
const userId = context.user?.id || context.userId; const userId = context.user?.id || context.userId;
if (!userId) { if (!userId) {
return { return {

View File

@ -6,8 +6,7 @@ import { resourceScopeResolvers } from "./scopeResolver";
// Fusionne les Query et Mutation des deux resolvers // Fusionne les Query et Mutation des deux resolvers
export const Resource = { export const Resource = {
typeDefs: [typeDefs, resourceScopeTypeDefs], typeDefs: [typeDefs, resourceScopeTypeDefs],
resolvers: [ resolvers: {
{
Query: { Query: {
...resourceResolvers.Query, ...resourceResolvers.Query,
...resourceScopeResolvers.Query, ...resourceScopeResolvers.Query,
@ -17,5 +16,4 @@ export const Resource = {
...resourceScopeResolvers.Mutation, ...resourceScopeResolvers.Mutation,
}, },
}, },
],
}; };

View File

@ -8,8 +8,9 @@ function getAccessTokenFromContext(context: GraphQLContext): string | null {
return context.accessToken || null; return context.accessToken || null;
} }
export async function listResources(context: GraphQLContext): Promise<Resource[]> { export async function listResources(_context: GraphQLContext): Promise<Resource[]> {
const token = getAccessTokenFromContext(context) || (await getLogtoAccessToken()); // Toujours utiliser le token M2M pour les endpoints admin
const token = await getLogtoAccessToken();
try { try {
const response = await axios.get(`${process.env.LOGTO_ENDPOINT}/api/resources`, { const response = await axios.get(`${process.env.LOGTO_ENDPOINT}/api/resources`, {
headers: { Authorization: `Bearer ${token}` }, headers: { Authorization: `Bearer ${token}` },
@ -18,10 +19,13 @@ export async function listResources(context: GraphQLContext): Promise<Resource[]
...resource, ...resource,
scopes: Array.isArray(resource.scopes) ? resource.scopes : [], scopes: Array.isArray(resource.scopes) ? resource.scopes : [],
})); }));
} catch { } catch (err) {
throw new GraphQLError("Erreur lors de la récupération des ressources", { if (axios.isAxiosError(err)) {
extensions: { code: "RESOURCES_FETCH_FAILED" }, console.error(`[listResources] Axios error:`, err.response?.data || err.message);
}); } else {
console.error(`[listResources] Unknown error:`, err);
}
return [];
} }
} }

View File

@ -24,7 +24,8 @@ async function fetchRolePermissions(
description: scope.description, description: scope.description,
})) }))
: []; : [];
} catch { } catch (err) {
console.error(`[fetchRolePermissions] error for role ${roleId}:`, err);
return []; return [];
} }
} }
@ -50,23 +51,31 @@ export async function listRoles(context: GraphQLContext): Promise<Role[]> {
} }
} }
export async function getUserRoles(userId: string, context: GraphQLContext): Promise<Role[]> { export async function getUserRoles(userId: string, _context: GraphQLContext): Promise<Role[]> {
const token = getAccessTokenFromContext(context) || (await getLogtoAccessToken()); // Toujours utiliser le token M2M pour les endpoints admin
const token = await getLogtoAccessToken();
try { try {
const response = await axios.get(`${process.env.LOGTO_ENDPOINT}/api/users/${userId}/roles`, { const response = await axios.get(`${process.env.LOGTO_ENDPOINT}/api/users/${userId}/roles`, {
headers: { Authorization: `Bearer ${token}` }, headers: { Authorization: `Bearer ${token}` },
}); });
const roles = response.data as Role[]; const roles = Array.isArray(response.data) ? (response.data as Role[]) : [];
if (!roles.length) {
console.warn(`[getUserRoles] Aucun rôle trouvé pour l'utilisateur ${userId}`);
return [];
}
return await Promise.all( return await Promise.all(
roles.map(async role => ({ roles.map(async role => ({
...role, ...role,
permissions: await fetchRolePermissions(role.id, token), permissions: await fetchRolePermissions(role.id, token),
})) }))
); );
} catch { } catch (err) {
throw new GraphQLError("Erreur lors de la récupération des rôles de l'utilisateur", { if (axios.isAxiosError(err)) {
extensions: { code: "USER_ROLES_FETCH_FAILED" }, console.error(`[getUserRoles] Axios error:`, err.response?.data || err.message);
}); } else {
console.error(`[getUserRoles] Unknown error:`, err);
}
return [];
} }
} }

View File

@ -1,4 +1,3 @@
import { patContextCache } from "@/graphql/context/features/patCache";
import axios from "axios"; import axios from "axios";
import { GraphQLError } from "graphql"; import { GraphQLError } from "graphql";
import { import {
@ -191,16 +190,14 @@ async function getUserByEmail(email: string): Promise<LogtoUser | null> {
} }
} }
// Cache pour les associations PAT -> userId pour éviter les recherches répétées
// Note: Maintenant géré via le contexte GraphQL (context.patCache)
async function getUserByPersonalAccessToken(pat: string, context: GraphQLContext): Promise<LogtoUser | null> { async function getUserByPersonalAccessToken(pat: string, context: GraphQLContext): Promise<LogtoUser | null> {
// Récupérer l'userId directement depuis le cache du contexte // Récupérer l'userId directement depuis le cache du contexte
let userId = context.patCache.getUserId(pat); let userId = context.userId;
// Si pas trouvé dans le cache, essayer de valider le PAT avec l'API Logto // Si pas trouvé dans le cache, essayer de valider le PAT avec l'API Logto
if (!userId) { if (!userId) {
userId = await validatePATWithLogtoAPI(pat, context); const validatedUserId = await validatePATWithLogtoAPI(pat, context);
userId = validatedUserId === null ? undefined : validatedUserId;
if (!userId) { if (!userId) {
throw new GraphQLError("Token d'accès personnel invalide ou non reconnu", { throw new GraphQLError("Token d'accès personnel invalide ou non reconnu", {
@ -219,8 +216,6 @@ async function getUserByPersonalAccessToken(pat: string, context: GraphQLContext
return response.data; return response.data;
} catch (error) { } catch (error) {
console.error("Erreur lors de la récupération de l'utilisateur par PAT:", error); console.error("Erreur lors de la récupération de l'utilisateur par PAT:", error);
// Si l'utilisateur n'existe plus, nettoyer le cache
context.patCache.remove(pat);
throw new GraphQLError("Utilisateur associé au token non trouvé", { throw new GraphQLError("Utilisateur associé au token non trouvé", {
extensions: { code: "PAT_USER_NOT_FOUND" }, extensions: { code: "PAT_USER_NOT_FOUND" },
}); });
@ -228,7 +223,7 @@ async function getUserByPersonalAccessToken(pat: string, context: GraphQLContext
} }
// Fonction pour valider un PAT avec l'API Logto et le remettre en cache // Fonction pour valider un PAT avec l'API Logto et le remettre en cache
async function validatePATWithLogtoAPI(pat: string, context: GraphQLContext): Promise<string | null> { async function validatePATWithLogtoAPI(pat: string, _context: GraphQLContext): Promise<string | null> {
const m2mToken = await getLogtoAccessToken(); const m2mToken = await getLogtoAccessToken();
try { try {
@ -260,8 +255,6 @@ async function validatePATWithLogtoAPI(pat: string, context: GraphQLContext): Pr
); );
if (matchingToken) { if (matchingToken) {
// Remettre en cache pour éviter de refaire cette recherche
context.patCache.register(pat, user.id, 24); // 24 heures
return user.id; return user.id;
} }
} catch { } catch {
@ -278,7 +271,7 @@ async function validatePATWithLogtoAPI(pat: string, context: GraphQLContext): Pr
} }
async function createPersonalAccessToken( async function createPersonalAccessToken(
userId: string, userId: string,
context: GraphQLContext _context: GraphQLContext
): Promise<{ token: string; expiresIn: number; userId: string }> { ): Promise<{ token: string; expiresIn: number; userId: string }> {
const m2mToken = await getLogtoAccessToken(); const m2mToken = await getLogtoAccessToken();
const expiresAtTimestamp = Date.now() + 365 * 24 * 60 * 60 * 1000; // 1 an en millisecondes const expiresAtTimestamp = Date.now() + 365 * 24 * 60 * 60 * 1000; // 1 an en millisecondes
@ -302,9 +295,6 @@ async function createPersonalAccessToken(
const { value: token, expiresAt } = response.data; const { value: token, expiresAt } = response.data;
const expiresIn = Math.floor((expiresAt - Date.now()) / 1000); // Convertir en secondes const expiresIn = Math.floor((expiresAt - Date.now()) / 1000); // Convertir en secondes
// Enregistrer l'association PAT -> userId dans le cache du contexte
context.patCache.register(token, userId, 24); // 24 heures
return { token, expiresIn, userId }; return { token, expiresIn, userId };
} catch (error) { } catch (error) {
console.error("Erreur lors de la création du PAT:", error); console.error("Erreur lors de la création du PAT:", error);
@ -347,9 +337,6 @@ async function createPersonalAccessToken(
console.warn("PAT existant trouvé et encore valide, réutilisation..."); console.warn("PAT existant trouvé et encore valide, réutilisation...");
const expiresIn = Math.floor((expiresAt - now) / 1000); const expiresIn = Math.floor((expiresAt - now) / 1000);
// Enregistrer l'association PAT -> userId dans le cache du contexte
context.patCache.register(existingPat.value, userId, 24);
return { return {
token: existingPat.value, token: existingPat.value,
expiresIn, expiresIn,
@ -383,9 +370,6 @@ async function createPersonalAccessToken(
const { value: token, expiresAt: newExpiresAt } = recreateResponse.data; const { value: token, expiresAt: newExpiresAt } = recreateResponse.data;
const expiresIn = Math.floor((newExpiresAt - Date.now()) / 1000); const expiresIn = Math.floor((newExpiresAt - Date.now()) / 1000);
// Enregistrer l'association PAT -> userId dans le cache du contexte
context.patCache.register(token, userId, 24);
return { token, expiresIn, userId }; return { token, expiresIn, userId };
} }
} }
@ -529,7 +513,6 @@ async function authenticateUserFallback(email: string, password: string): Promis
export const resolvers = { export const resolvers = {
Query: { Query: {
async me(_: unknown, __: unknown, context: GraphQLContext) { async me(_: unknown, __: unknown, context: GraphQLContext) {
// Priorité 1: Utilisation du Personal Access Token (PAT)
if (context.personalAccessToken) { if (context.personalAccessToken) {
const user = await getUserByPersonalAccessToken(context.personalAccessToken, context); const user = await getUserByPersonalAccessToken(context.personalAccessToken, context);
if (user) { if (user) {
@ -583,16 +566,6 @@ export const resolvers = {
}); });
} }
}, },
// Query de debug pour les statistiques du cache PAT
async patCacheStats(_: unknown, __: unknown, context: GraphQLContext) {
const stats = context.patCache.getStats();
return {
cacheSize: stats.size,
totalEntries: stats.entries,
timestamp: new Date().toISOString(),
};
},
}, },
Mutation: { Mutation: {
@ -623,7 +596,6 @@ export const resolvers = {
if (tokenData) { if (tokenData) {
accessToken = tokenData.token; accessToken = tokenData.token;
tokenExpiry = tokenData.expiresIn; tokenExpiry = tokenData.expiresIn;
patContextCache.register(accessToken, user.id, 24);
} }
} catch { } catch {
// On continue même si la création du token échoue // On continue même si la création du token échoue
@ -707,7 +679,6 @@ export const resolvers = {
if (tokenData) { if (tokenData) {
accessToken = tokenData.token; accessToken = tokenData.token;
tokenExpiry = tokenData.expiresIn; tokenExpiry = tokenData.expiresIn;
context.patCache.register(accessToken, user.id, 24); // Enregistre dans le cache du contexte
context.personalAccessToken = accessToken; context.personalAccessToken = accessToken;
context.userId = user.id; context.userId = user.id;
context.user = user; context.user = user;
@ -768,7 +739,6 @@ export const resolvers = {
} catch (error) { } catch (error) {
console.error("Erreur lors de la suppression du PAT dans Logto:", error); console.error("Erreur lors de la suppression du PAT dans Logto:", error);
} }
context.patCache.remove(context.personalAccessToken);
} }
return { return {
@ -785,4 +755,4 @@ export const resolvers = {
}, },
}; };
export { getLogtoAccessToken }; export { getLogtoAccessToken, validatePATWithLogtoAPI };

View File

@ -14,7 +14,7 @@ export const yoga = createYoga<GraphQLContext>({
plugins: [ plugins: [
// eslint-disable-next-line react-hooks/rules-of-hooks // eslint-disable-next-line react-hooks/rules-of-hooks
useCSRFPrevention({ useCSRFPrevention({
requestHeaders: ["x-graphql-yoga-csrf"], // default requestHeaders: ["x-graphql-yoga-csrf"],
}), }),
depthLimitPlugin, depthLimitPlugin,
introspectionPlugin, introspectionPlugin,