diff --git a/app/lib/directus.ts b/app/lib/directus.ts index 950e23c..d17bdc1 100644 --- a/app/lib/directus.ts +++ b/app/lib/directus.ts @@ -10,18 +10,21 @@ const BASE = ( "" ).replace(/\/$/, ""); // DIRECTUS_TOKEN_ADMIN_REGISTER is the canonical credential for the production -// Registration Bot policy. Keep the older aliases as fallbacks so an existing -// deployment does not silently lose authentication during migration. -const TOKEN_ADMIN_REGISTER = - process.env.DIRECTUS_TOKEN_ADMIN_REGISTER || - process.env.DIRECTUS_SERVICE_TOKEN || - process.env.DIRECTUS_ADMIN_TOKEN || - ""; // server-only +// Registration Bot policy. Keep older aliases as independently tested fallbacks: +// a configured but stale token must not hide a later valid credential. +const REGISTRATION_CREDENTIALS = [ + ["DIRECTUS_TOKEN_ADMIN_REGISTER", process.env.DIRECTUS_TOKEN_ADMIN_REGISTER], + ["DIRECTUS_SERVICE_TOKEN", process.env.DIRECTUS_SERVICE_TOKEN], + ["DIRECTUS_ADMIN_TOKEN", process.env.DIRECTUS_ADMIN_TOKEN], +].filter( + (entry, index, entries): entry is [string, string] => + Boolean(entry[1]) && entries.findIndex((other) => other[1] === entry[1]) === index +); const ROLE_MEMBER_NAME = process.env.DIRECTUS_ROLE_MEMBER_NAME || "Users"; const PROJECTS_COLLECTION = process.env.DIRECTUS_PROJECTS_COLLECTION || "projects"; if (!BASE) console.warn("[directus] Missing DIRECTUS_URL / NEXT_PUBLIC_API_BASE_URL"); -if (!TOKEN_ADMIN_REGISTER) +if (!REGISTRATION_CREDENTIALS.length) console.warn( "[directus] Missing DIRECTUS_TOKEN_ADMIN_REGISTER (or legacy service-token alias) " + "used for registration and username login" @@ -134,17 +137,33 @@ export async function dxDELETE(path: string, bearer: string): Promise(path: string, init?: RequestInit): Promise { if (!BASE) throw new Error("Missing Directus base URL"); - if (!TOKEN_ADMIN_REGISTER) throw new Error("Missing Directus registration credential"); - const res = await fetch(`${BASE}${path}`, { - ...init, - headers: { - Accept: "application/json", - Authorization: `Bearer ${TOKEN_ADMIN_REGISTER}`, - ...(init?.headers || {}), - }, - cache: "no-store", - }); - return (await throwIfNotOk(res)) as T; + if (!REGISTRATION_CREDENTIALS.length) throw new Error("Missing Directus registration credential"); + + let lastAuthorizationError: any = null; + for (const [credentialName, credential] of REGISTRATION_CREDENTIALS) { + const res = await fetch(`${BASE}${path}`, { + ...init, + headers: { + Accept: "application/json", + Authorization: `Bearer ${credential}`, + ...(init?.headers || {}), + }, + cache: "no-store", + }); + + try { + return (await throwIfNotOk(res)) as T; + } catch (error: any) { + if (error?.status !== 401 && error?.status !== 403) throw error; + lastAuthorizationError = error; + console.warn(`[directus] Registration credential rejected: ${credentialName}`, { + status: error.status, + code: error?.detail?.errors?.[0]?.extensions?.code, + }); + } + } + + throw lastAuthorizationError ?? new Error("No authorized Directus registration credential"); } // ─────────────────────────────────────────────────────────────