Refactor GraphQL API routes and remove unused components; enhance error handling and context management
This commit is contained in:
parent
f1f1b45c91
commit
7d0a6e5a74
@ -1,3 +1,5 @@
|
||||
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: {} });
|
||||
|
||||
@ -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'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'inscription
|
||||
</a>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -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`
|
||||
mutation DeleteConnector($id: ID!) {
|
||||
deleteConnector(id: $id) {
|
||||
|
||||
@ -23,7 +23,7 @@ export default function RolesPage() {
|
||||
// Fetch roles
|
||||
useEffect(() => {
|
||||
client
|
||||
.request(gql`
|
||||
.request<{ roles: Role[] }>(gql`
|
||||
{
|
||||
roles {
|
||||
id
|
||||
@ -37,7 +37,7 @@ export default function RolesPage() {
|
||||
}
|
||||
}
|
||||
`)
|
||||
.then((data: any) => {
|
||||
.then(data => {
|
||||
setRoles(data.roles);
|
||||
setLoading(false);
|
||||
})
|
||||
|
||||
@ -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 data: { createResourceScope: Permission } = await client.request(mutation, variables);
|
||||
setScopes([...scopes, data.createResourceScope]);
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import setAuth from "@/graphql/context/features/auth";
|
||||
import initGuard from "@/graphql/context/features/guard";
|
||||
import { patContextCache } from "@/graphql/context/features/patCache";
|
||||
import { GraphQLContext } from "@/graphql/context/types";
|
||||
import { YogaInitialContext } from "graphql-yoga";
|
||||
|
||||
@ -13,7 +12,6 @@ export async function context({ request }: YogaInitialContext): Promise<GraphQLC
|
||||
request,
|
||||
...auth,
|
||||
guard,
|
||||
patCache: patContextCache,
|
||||
};
|
||||
|
||||
return context;
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { AuthTypes } from "@/graphql/context";
|
||||
import { patContextCache } from "@/graphql/context/features/patCache";
|
||||
import { listResources, Resource } from "@/graphql/features/Resource/resourceService";
|
||||
import { getUserRoles, Role } from "@/graphql/features/Role/roleService";
|
||||
import { validatePATWithLogtoAPI } from "@/graphql/features/User/resolver";
|
||||
@ -69,7 +68,7 @@ const setAuth = async (
|
||||
if (token.includes(".") && token.split(".").length === 3) {
|
||||
// C'est un JWT
|
||||
const decoded = decodeJWT(token);
|
||||
userId = decoded?.sub as string | undefined;
|
||||
userId = decoded?.sub;
|
||||
if (userId) {
|
||||
user = {
|
||||
id: userId,
|
||||
@ -79,27 +78,23 @@ const setAuth = async (
|
||||
isActive: typeof decoded?.isActive === "boolean" ? decoded.isActive : undefined,
|
||||
};
|
||||
}
|
||||
// Log JWT
|
||||
console.error("[setAuth] JWT decoded:", decoded);
|
||||
} 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 {
|
||||
userId = await validatePATWithLogtoAPI(token, { patCache: patContextCache, db: {}, request: req });
|
||||
if (userId) {
|
||||
patContextCache.register(token, userId, 24);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("[setAuth] PAT non reconnu via Logto:", token, e);
|
||||
try {
|
||||
let requestObj: Request;
|
||||
if (req instanceof Request) {
|
||||
requestObj = req;
|
||||
} else {
|
||||
// Create a minimal Request object if possible, or throw an error
|
||||
throw new Error("PAT validation requires a Request object.");
|
||||
}
|
||||
}
|
||||
console.error("[setAuth] PAT lookup:", { token, userId });
|
||||
if (userId) {
|
||||
user = { id: userId };
|
||||
} else {
|
||||
console.error("[setAuth] PAT non reconnu dans le cache ni via Logto, token:", token);
|
||||
userId = (await validatePATWithLogtoAPI(token, { db: {}, request: requestObj })) || undefined;
|
||||
if (userId) {
|
||||
user = { id: userId };
|
||||
} else {
|
||||
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,
|
||||
request: req as Request,
|
||||
db: {},
|
||||
patCache: patContextCache,
|
||||
};
|
||||
roles = await getUserRoles(userId, minimalContext);
|
||||
permissions = roles.flatMap(r =>
|
||||
|
||||
@ -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
|
||||
@ -1,11 +1,9 @@
|
||||
import type { Resource } from "@/graphql/features/Resource/resourceService";
|
||||
import type { Role } from "@/graphql/features/Role/roleService";
|
||||
|
||||
export interface DB {
|
||||
// Define your database types here
|
||||
}
|
||||
export type DB = object;
|
||||
|
||||
export interface Guard {}
|
||||
export type Guard = object;
|
||||
|
||||
export interface GuardFunction {
|
||||
(auth?: AuthTypes): Guard;
|
||||
@ -50,5 +48,4 @@ export interface GraphQLContext {
|
||||
accessToken?: string;
|
||||
personalAccessToken?: string;
|
||||
userId?: string;
|
||||
patCache: PATCache; // Cache PAT intégré au contexte
|
||||
}
|
||||
|
||||
@ -192,12 +192,6 @@ export const organizationResolvers = {
|
||||
context: GraphQLContext
|
||||
): Promise<OrganizationsListResponse> {
|
||||
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;
|
||||
if (!userId) {
|
||||
return {
|
||||
|
||||
@ -6,16 +6,14 @@ import { resourceScopeResolvers } from "./scopeResolver";
|
||||
// Fusionne les Query et Mutation des deux resolvers
|
||||
export const Resource = {
|
||||
typeDefs: [typeDefs, resourceScopeTypeDefs],
|
||||
resolvers: [
|
||||
{
|
||||
Query: {
|
||||
...resourceResolvers.Query,
|
||||
...resourceScopeResolvers.Query,
|
||||
},
|
||||
Mutation: {
|
||||
...resourceResolvers.Mutation,
|
||||
...resourceScopeResolvers.Mutation,
|
||||
},
|
||||
resolvers: {
|
||||
Query: {
|
||||
...resourceResolvers.Query,
|
||||
...resourceScopeResolvers.Query,
|
||||
},
|
||||
],
|
||||
Mutation: {
|
||||
...resourceResolvers.Mutation,
|
||||
...resourceScopeResolvers.Mutation,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@ -8,8 +8,9 @@ function getAccessTokenFromContext(context: GraphQLContext): string | null {
|
||||
return context.accessToken || null;
|
||||
}
|
||||
|
||||
export async function listResources(context: GraphQLContext): Promise<Resource[]> {
|
||||
const token = getAccessTokenFromContext(context) || (await getLogtoAccessToken());
|
||||
export async function listResources(_context: GraphQLContext): Promise<Resource[]> {
|
||||
// Toujours utiliser le token M2M pour les endpoints admin
|
||||
const token = await getLogtoAccessToken();
|
||||
try {
|
||||
const response = await axios.get(`${process.env.LOGTO_ENDPOINT}/api/resources`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
@ -18,10 +19,13 @@ export async function listResources(context: GraphQLContext): Promise<Resource[]
|
||||
...resource,
|
||||
scopes: Array.isArray(resource.scopes) ? resource.scopes : [],
|
||||
}));
|
||||
} catch {
|
||||
throw new GraphQLError("Erreur lors de la récupération des ressources", {
|
||||
extensions: { code: "RESOURCES_FETCH_FAILED" },
|
||||
});
|
||||
} catch (err) {
|
||||
if (axios.isAxiosError(err)) {
|
||||
console.error(`[listResources] Axios error:`, err.response?.data || err.message);
|
||||
} else {
|
||||
console.error(`[listResources] Unknown error:`, err);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -24,7 +24,8 @@ async function fetchRolePermissions(
|
||||
description: scope.description,
|
||||
}))
|
||||
: [];
|
||||
} catch {
|
||||
} catch (err) {
|
||||
console.error(`[fetchRolePermissions] error for role ${roleId}:`, err);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@ -50,23 +51,31 @@ export async function listRoles(context: GraphQLContext): Promise<Role[]> {
|
||||
}
|
||||
}
|
||||
|
||||
export async function getUserRoles(userId: string, context: GraphQLContext): Promise<Role[]> {
|
||||
const token = getAccessTokenFromContext(context) || (await getLogtoAccessToken());
|
||||
export async function getUserRoles(userId: string, _context: GraphQLContext): Promise<Role[]> {
|
||||
// Toujours utiliser le token M2M pour les endpoints admin
|
||||
const token = await getLogtoAccessToken();
|
||||
try {
|
||||
const response = await axios.get(`${process.env.LOGTO_ENDPOINT}/api/users/${userId}/roles`, {
|
||||
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(
|
||||
roles.map(async role => ({
|
||||
...role,
|
||||
permissions: await fetchRolePermissions(role.id, token),
|
||||
}))
|
||||
);
|
||||
} catch {
|
||||
throw new GraphQLError("Erreur lors de la récupération des rôles de l'utilisateur", {
|
||||
extensions: { code: "USER_ROLES_FETCH_FAILED" },
|
||||
});
|
||||
} catch (err) {
|
||||
if (axios.isAxiosError(err)) {
|
||||
console.error(`[getUserRoles] Axios error:`, err.response?.data || err.message);
|
||||
} else {
|
||||
console.error(`[getUserRoles] Unknown error:`, err);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import { patContextCache } from "@/graphql/context/features/patCache";
|
||||
import axios from "axios";
|
||||
import { GraphQLError } from "graphql";
|
||||
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> {
|
||||
// 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
|
||||
if (!userId) {
|
||||
userId = await validatePATWithLogtoAPI(pat, context);
|
||||
const validatedUserId = await validatePATWithLogtoAPI(pat, context);
|
||||
userId = validatedUserId === null ? undefined : validatedUserId;
|
||||
|
||||
if (!userId) {
|
||||
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;
|
||||
} catch (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é", {
|
||||
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
|
||||
async function validatePATWithLogtoAPI(pat: string, context: GraphQLContext): Promise<string | null> {
|
||||
async function validatePATWithLogtoAPI(pat: string, _context: GraphQLContext): Promise<string | null> {
|
||||
const m2mToken = await getLogtoAccessToken();
|
||||
|
||||
try {
|
||||
@ -260,8 +255,6 @@ async function validatePATWithLogtoAPI(pat: string, context: GraphQLContext): Pr
|
||||
);
|
||||
|
||||
if (matchingToken) {
|
||||
// Remettre en cache pour éviter de refaire cette recherche
|
||||
context.patCache.register(pat, user.id, 24); // 24 heures
|
||||
return user.id;
|
||||
}
|
||||
} catch {
|
||||
@ -278,7 +271,7 @@ async function validatePATWithLogtoAPI(pat: string, context: GraphQLContext): Pr
|
||||
}
|
||||
async function createPersonalAccessToken(
|
||||
userId: string,
|
||||
context: GraphQLContext
|
||||
_context: GraphQLContext
|
||||
): Promise<{ token: string; expiresIn: number; userId: string }> {
|
||||
const m2mToken = await getLogtoAccessToken();
|
||||
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 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 };
|
||||
} catch (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...");
|
||||
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 {
|
||||
token: existingPat.value,
|
||||
expiresIn,
|
||||
@ -383,9 +370,6 @@ async function createPersonalAccessToken(
|
||||
const { value: token, expiresAt: newExpiresAt } = recreateResponse.data;
|
||||
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 };
|
||||
}
|
||||
}
|
||||
@ -529,7 +513,6 @@ async function authenticateUserFallback(email: string, password: string): Promis
|
||||
export const resolvers = {
|
||||
Query: {
|
||||
async me(_: unknown, __: unknown, context: GraphQLContext) {
|
||||
// Priorité 1: Utilisation du Personal Access Token (PAT)
|
||||
if (context.personalAccessToken) {
|
||||
const user = await getUserByPersonalAccessToken(context.personalAccessToken, context);
|
||||
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: {
|
||||
@ -623,7 +596,6 @@ export const resolvers = {
|
||||
if (tokenData) {
|
||||
accessToken = tokenData.token;
|
||||
tokenExpiry = tokenData.expiresIn;
|
||||
patContextCache.register(accessToken, user.id, 24);
|
||||
}
|
||||
} catch {
|
||||
// On continue même si la création du token échoue
|
||||
@ -707,7 +679,6 @@ export const resolvers = {
|
||||
if (tokenData) {
|
||||
accessToken = tokenData.token;
|
||||
tokenExpiry = tokenData.expiresIn;
|
||||
context.patCache.register(accessToken, user.id, 24); // Enregistre dans le cache du contexte
|
||||
context.personalAccessToken = accessToken;
|
||||
context.userId = user.id;
|
||||
context.user = user;
|
||||
@ -768,7 +739,6 @@ export const resolvers = {
|
||||
} catch (error) {
|
||||
console.error("Erreur lors de la suppression du PAT dans Logto:", error);
|
||||
}
|
||||
context.patCache.remove(context.personalAccessToken);
|
||||
}
|
||||
|
||||
return {
|
||||
@ -785,4 +755,4 @@ export const resolvers = {
|
||||
},
|
||||
};
|
||||
|
||||
export { getLogtoAccessToken };
|
||||
export { getLogtoAccessToken, validatePATWithLogtoAPI };
|
||||
|
||||
@ -14,7 +14,7 @@ export const yoga = createYoga<GraphQLContext>({
|
||||
plugins: [
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
useCSRFPrevention({
|
||||
requestHeaders: ["x-graphql-yoga-csrf"], // default
|
||||
requestHeaders: ["x-graphql-yoga-csrf"],
|
||||
}),
|
||||
depthLimitPlugin,
|
||||
introspectionPlugin,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user