From 0fd0cda4054bfc0e4f8b561899ac3e3d11016885 Mon Sep 17 00:00:00 2001 From: Elias Bennour Date: Sun, 25 May 2025 16:01:03 +0200 Subject: [PATCH] update nextauth --- prisma/schema.prisma | 16 +++++++-------- src/pages/api/auth/[...nextauth].ts | 32 +++++++++++++++++++---------- 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index c47e4e9..484fccf 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -12,24 +12,24 @@ datasource db { model Account { id String @id @default(cuid()) userId String - type String - provider String - providerAccountId String + type String // z.B. "oauth", "oidc" + provider String // z.B. "keycloak", "credentials" + providerAccountId String // Die ID des Benutzers beim Provider (z.B. Keycloak 'sub') refresh_token String? @db.Text access_token String? @db.Text - expires_at Int? // Üblicherweise für access_token Ablauf (in Sekunden seit Unix-Epoche) - token_type String? + expires_at Int? // Unix-Timestamp (in Sekunden) für den Ablauf des access_token + token_type String? // z.B. "Bearer" scope String? id_token String? @db.Text session_state String? - refresh_expires_in Int? // NEU: Lebensdauer des Refresh Tokens in Sekunden - not_before_policy Int? // NEU: Keycloak "Not Before Policy" Wert (Unix-Timestamp) + refresh_expires_in Int? // Lebensdauer des Refresh Tokens in Sekunden + not_before_policy Int? // NEU: Keycloak "Not Before Policy" Wert (oft ein Timestamp) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([provider, providerAccountId]) - @@index([userId]) // Index für userId hinzugefügt für bessere Abfrageleistung + @@index([userId]) } model Session { diff --git a/src/pages/api/auth/[...nextauth].ts b/src/pages/api/auth/[...nextauth].ts index 82f7cc6..b9b8749 100644 --- a/src/pages/api/auth/[...nextauth].ts +++ b/src/pages/api/auth/[...nextauth].ts @@ -7,15 +7,14 @@ import { PrismaAdapter } from "@next-auth/prisma-adapter"; import prisma from "../../../lib/prisma"; import bcrypt from "bcryptjs"; -import type { User } from "next-auth"; +import type { User, Account } from "next-auth"; // Account importieren für den signIn Callback export const authOptions: AuthOptions = { adapter: PrismaAdapter(prisma), session: { strategy: "jwt", }, - // Debug-Modus für ausführlichere Logs von NextAuth.js aktivieren - debug: true, // process.env.NODE_ENV === 'development', + debug: process.env.NODE_ENV === 'development', providers: [ KeycloakProvider({ clientId: process.env.KEYCLOAK_CLIENT_ID as string, @@ -23,14 +22,11 @@ export const authOptions: AuthOptions = { issuer: process.env.KEYCLOAK_ISSUER as string, allowDangerousEmailAccountLinking: true, profile(profile: KeycloakProfile, tokens: TokenSet): User | Promise { - // Detailliertes Logging des empfangenen Keycloak-Profils und der Tokens console.log("[NextAuth.js] Keycloak Profile Received:", JSON.stringify(profile, null, 2)); - // Vorsicht beim Loggen von Tokens in der Produktion, hier nur ein Snippet des ID-Tokens if (tokens.id_token) { console.log("[NextAuth.js] Keycloak ID Token Snippet:", tokens.id_token.substring(0, 50) + "..."); } - let userRole = 'user'; if (profile.realm_access?.roles && Array.isArray(profile.realm_access.roles) && profile.realm_access.roles.includes('admin')) { userRole = 'admin'; @@ -38,22 +34,20 @@ export const authOptions: AuthOptions = { userRole = 'admin'; } - const emailFromProvider = profile.email?.toLowerCase(); // E-Mail normalisieren (zu Kleinbuchstaben) + const emailFromProvider = profile.email?.toLowerCase(); if (!emailFromProvider) { console.error("[NextAuth.js] Email not provided by Keycloak profile. Profile data:", profile); - // Du könntest hier entscheiden, einen Fehler zu werfen oder einen Fallback zu implementieren, - // aber für die Kontoverknüpfung ist eine E-Mail essentiell. throw new Error("E-Mail wurde nicht vom Identitätsprovider (Keycloak) bereitgestellt."); } const userToReturn: User = { id: profile.sub, name: profile.name || profile.preferred_username, - email: emailFromProvider, // Normalisierte E-Mail verwenden + email: emailFromProvider, image: profile.picture, role: userRole, - isApproved: true, // Annahme: OIDC-Benutzer sind standardmäßig freigegeben + isApproved: true, }; console.log("[NextAuth.js] User object to be processed by adapter/callbacks:", JSON.stringify(userToReturn, null, 2)); @@ -95,6 +89,22 @@ export const authOptions: AuthOptions = { }), ], callbacks: { + // NEU: signIn Callback, um Account-Daten vor dem Speichern anzupassen + async signIn({account}) { + if (account && account.provider === "keycloak") { + // Prüfe, ob 'not-before-policy' im Account-Objekt von Keycloak vorhanden ist + // und benenne es in 'not_before_policy' um, damit es zum Prisma-Schema passt. + // Das account-Objekt hier enthält die rohen Daten vom Token-Endpunkt. + const keycloakAccount = account as Account & { 'not-before-policy'?: any, 'not_before_policy'?: any }; + + if (keycloakAccount['not-before-policy'] !== undefined) { + console.log(`[NextAuth.js] signIn callback: Renaming 'not-before-policy' to 'not_before_policy' for account: ${account.providerAccountId}`); + keycloakAccount.not_before_policy = keycloakAccount['not-before-policy']; + delete keycloakAccount['not-before-policy']; + } + } + return true; // Erlaube den Anmeldevorgang + }, async jwt({ token, user, account }) { if (account && user) { token.id = user.id;