diff --git a/src/app/api/graphql/route.ts b/src/app/api/graphql/route.ts
index 151ee38..e18c9d9 100644
--- a/src/app/api/graphql/route.ts
+++ b/src/app/api/graphql/route.ts
@@ -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: {} });
diff --git a/src/app/callback/page.tsx b/src/app/callback/page.tsx
deleted file mode 100644
index 4676ca5..0000000
--- a/src/app/callback/page.tsx
+++ /dev/null
@@ -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 (
-
-
-
- {status === "loading" && (
- <>
-
-
Authentification en cours...
-
Veuillez patienter pendant que nous traitons votre authentification.
- >
- )}
-
- {status === "success" && (
- <>
-
-
Authentification réussie !
-
{message}
-
Redirection en cours...
- >
- )}
-
- {status === "error" && (
- <>
-
-
Erreur d'authentification
-
{message}
-
- >
- )}
-
-
-
- );
-}
diff --git a/src/app/connectors/page.tsx b/src/app/connectors/page.tsx
index e0c6037..fcc2234 100644
--- a/src/app/connectors/page.tsx
+++ b/src/app/connectors/page.tsx
@@ -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) {
diff --git a/src/app/roles/page.tsx b/src/app/roles/page.tsx
index 9730644..24bdd31 100644
--- a/src/app/roles/page.tsx
+++ b/src/app/roles/page.tsx
@@ -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);
})
diff --git a/src/app/roles/permissions.tsx b/src/app/roles/permissions.tsx
index 483dd75..7191fad 100644
--- a/src/app/roles/permissions.tsx
+++ b/src/app/roles/permissions.tsx
@@ -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]);
diff --git a/src/app/test-signup/page.tsx b/src/app/test-signup/page.tsx
deleted file mode 100644
index e69de29..0000000
diff --git a/src/graphql/context/context.ts b/src/graphql/context/context.ts
index 0be8ce9..f13d660 100644
--- a/src/graphql/context/context.ts
+++ b/src/graphql/context/context.ts
@@ -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
diff --git a/src/graphql/context/features/patCache.ts b/src/graphql/context/features/patCache.ts
deleted file mode 100644
index 8fce554..0000000
--- a/src/graphql/context/features/patCache.ts
+++ /dev/null
@@ -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 = 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
diff --git a/src/graphql/context/types.ts b/src/graphql/context/types.ts
index e857368..1aeae54 100644
--- a/src/graphql/context/types.ts
+++ b/src/graphql/context/types.ts
@@ -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
}
diff --git a/src/graphql/features/Organization/resolver.ts b/src/graphql/features/Organization/resolver.ts
index a763281..441e014 100644
--- a/src/graphql/features/Organization/resolver.ts
+++ b/src/graphql/features/Organization/resolver.ts
@@ -192,12 +192,6 @@ export const organizationResolvers = {
context: GraphQLContext
): Promise {
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 {
diff --git a/src/graphql/features/Resource/index.ts b/src/graphql/features/Resource/index.ts
index dfd80b0..423ec9f 100644
--- a/src/graphql/features/Resource/index.ts
+++ b/src/graphql/features/Resource/index.ts
@@ -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,
+ },
+ },
};
diff --git a/src/graphql/features/Resource/resourceService.ts b/src/graphql/features/Resource/resourceService.ts
index 318bb88..244371c 100644
--- a/src/graphql/features/Resource/resourceService.ts
+++ b/src/graphql/features/Resource/resourceService.ts
@@ -8,8 +8,9 @@ function getAccessTokenFromContext(context: GraphQLContext): string | null {
return context.accessToken || null;
}
-export async function listResources(context: GraphQLContext): Promise {
- const token = getAccessTokenFromContext(context) || (await getLogtoAccessToken());
+export async function listResources(_context: GraphQLContext): Promise {
+ // 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 {
}
}
-export async function getUserRoles(userId: string, context: GraphQLContext): Promise {
- const token = getAccessTokenFromContext(context) || (await getLogtoAccessToken());
+export async function getUserRoles(userId: string, _context: GraphQLContext): Promise {
+ // 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 [];
}
}
diff --git a/src/graphql/features/User/resolver.ts b/src/graphql/features/User/resolver.ts
index 8e30825..567483f 100644
--- a/src/graphql/features/User/resolver.ts
+++ b/src/graphql/features/User/resolver.ts
@@ -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 {
}
}
-// 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 {
// 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 {
+async function validatePATWithLogtoAPI(pat: string, _context: GraphQLContext): Promise {
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 };
diff --git a/src/graphql/index.ts b/src/graphql/index.ts
index 4b19962..a529c96 100644
--- a/src/graphql/index.ts
+++ b/src/graphql/index.ts
@@ -14,7 +14,7 @@ export const yoga = createYoga({
plugins: [
// eslint-disable-next-line react-hooks/rules-of-hooks
useCSRFPrevention({
- requestHeaders: ["x-graphql-yoga-csrf"], // default
+ requestHeaders: ["x-graphql-yoga-csrf"],
}),
depthLimitPlugin,
introspectionPlugin,