Files
CollectFlow/src/lib/auth.ts
T
Claude 73c74bf579 Merge de main : report du masquage des secrets dans la nouvelle page Paramètres
La page Paramètres a été déplacée dans settings/client.tsx (onglets). Le
conflit sur page.tsx est résolu en gardant la version de main ; les
changements de l'interface y sont reportés : champs mot de passe
PostgreSQL et Qlik vides avec « Enregistré — inchangé si vide », test Qlik
possible avec un mot de passe enregistré, plus de mot de passe relu depuis
le serveur.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RkfUui3qGg2zmu3uHWjNXJ
2026-10-04 06:40:22 +00:00

91 lines
3.6 KiB
TypeScript

import NextAuth from "next-auth";
import Credentials from "next-auth/providers/credentials";
import { db } from "@/db";
import { users } from "@/db/schema";
import { eq } from "drizzle-orm";
import { verifyPassword, getFallbackUsers } from "@/features/auth/logic/auth-logic";
import { ensureAdminExists } from "@/features/auth/logic/seed-admin";
import { authConfig } from "./auth.config";
export const { handlers, auth, signIn, signOut } = NextAuth({
...authConfig,
providers: [
Credentials({
name: "Credentials",
credentials: {
username: { label: "Utilisateur", type: "text" },
password: { label: "Mot de passe", type: "password" }
},
async authorize(credentials) {
await ensureAdminExists();
if (!credentials?.username || !credentials?.password) return null;
try {
console.log(`[AUTH] Checking user "${credentials.username}"...`);
let user: any = null;
// Try DB first
try {
const [dbUser] = await db.select()
.from(users)
.where(eq(users.username, credentials.username as string));
user = dbUser;
console.log(`[AUTH] DB check: User found: ${!!user}`);
} catch (dbErr) {
console.warn("[AUTH] DB unreachable, falling back to JSON.");
}
// Fallback to JSON if DB failed or user not found
if (!user) {
const fallbackUsers = getFallbackUsers();
user = fallbackUsers.find(u => u.username === credentials.username);
console.log(`[AUTH] JSON check: User found: ${!!user}`);
}
if (!user) {
console.warn(`[AUTH] User "${credentials.username}" not found anywhere.`);
return null;
}
const isValid = verifyPassword(credentials.password as string, user.passwordHash);
console.log(`[AUTH] Password valid for "${credentials.username}": ${isValid}`);
if (!isValid) {
console.warn(`[AUTH] Invalid password for user "${credentials.username}".`);
return null;
}
return {
id: user.id.toString(),
// `name` est repris dans la session : sans lui, l'en-tête ne
// pouvait pas afficher qui est connecté.
name: user.username,
username: user.username,
role: user.role,
};
} catch (err: any) {
console.error("[AUTH] CRITICAL ERROR during authorize callback:", err.message || err);
return null;
}
}
})
],
});
/**
* Garde des actions serveur réservées aux administrateurs.
*
* Une action serveur est appelable directement par n'importe quel client connecté :
* la restriction de `/settings` dans le middleware ne la protège pas. Lève si la
* session n'est pas admin et renvoie la session sinon.
*/
export async function requireAdmin() {
const session = await auth();
if ((session?.user as { role?: string } | undefined)?.role !== "admin") {
throw new Error("Accès refusé : Droits administrateur requis.");
}
return session;
}