From b1c92b41cf427697fb08e80f97b4570d239dae7f Mon Sep 17 00:00:00 2001 From: Elias Bennour Date: Sun, 25 May 2025 15:23:03 +0200 Subject: [PATCH] update nextauth --- src/pages/api/auth/[...nextauth].ts | 51 ++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/src/pages/api/auth/[...nextauth].ts b/src/pages/api/auth/[...nextauth].ts index c98f258..b8f1228 100644 --- a/src/pages/api/auth/[...nextauth].ts +++ b/src/pages/api/auth/[...nextauth].ts @@ -1,15 +1,12 @@ // Datei: pages/api/auth/[...nextauth].ts -import NextAuth, {AuthOptions} from "next-auth"; +import NextAuth, { AuthOptions } from "next-auth"; import CredentialsProvider from "next-auth/providers/credentials"; import KeycloakProvider, { KeycloakProfile } from "next-auth/providers/keycloak"; -// Falls 'next-auth/jwt' nicht funktioniert, versuche 'next-auth/core/types' import { PrismaAdapter } from "@next-auth/prisma-adapter"; import prisma from "../../../lib/prisma"; import bcrypt from "bcryptjs"; -// User-Typ aus deinen globalen NextAuth-Typen (next-auth.d.ts) -// wird für den Rückgabetyp des profile-Callbacks benötigt. import type { User } from "next-auth"; export const authOptions: AuthOptions = { @@ -22,30 +19,29 @@ export const authOptions: AuthOptions = { clientId: process.env.KEYCLOAK_CLIENT_ID as string, clientSecret: process.env.KEYCLOAK_CLIENT_SECRET as string, issuer: process.env.KEYCLOAK_ISSUER as string, + allowDangerousEmailAccountLinking: true, profile(profile: KeycloakProfile): User | Promise { - // profile: Das von Keycloak zurückgegebene Benutzerprofil. - // tokens: Enthält access_token, id_token etc. und entspricht dem TokenSet-Typ. - let userRole = 'user'; // Standardrolle - // Beispiel für Rollenextraktion (passe dies an deine Keycloak-Konfiguration an): - // Oft sind Rollen in 'realm_access.roles' oder 'resource_access.[client-id].roles' - // im dekodierten Access Token oder ID Token. - // Oder Keycloak Mappers können Rollen direkt ins Profil-Objekt legen. + let userRole = 'user'; if (profile.realm_access?.roles && Array.isArray(profile.realm_access.roles) && profile.realm_access.roles.includes('admin')) { userRole = 'admin'; - } else if (profile.roles && Array.isArray(profile.roles) && profile.roles.includes('admin')) { // Falls 'roles' direkt im Profil ist + } else if (profile.roles && Array.isArray(profile.roles) && profile.roles.includes('admin')) { userRole = 'admin'; } + // Wenn ein bestehender Benutzer mit derselben E-Mail gefunden wird, + // wird der Adapter versuchen, dieses Keycloak-Konto mit dem bestehenden Benutzer zu verknüpfen. + // Die hier zurückgegebenen Daten können verwendet werden, um den bestehenden Benutzer zu aktualisieren. return { id: profile.sub, name: profile.name || profile.preferred_username, email: profile.email, image: profile.picture, role: userRole, - isApproved: true, - // emailVerified wird vom PrismaAdapter erwartet (kann null sein). - // Mappe es, wenn Keycloak 'email_verified: true' sendet. + isApproved: true, // Annahme: OIDC-Benutzer sind standardmäßig freigegeben + // Wenn ein lokales Konto existiert, wird dessen isApproved-Status + // möglicherweise durch die Adapterlogik beibehalten oder aktualisiert. + // Dies hängt vom genauen Verhalten des Adapters beim Verknüpfen ab. ...(profile.email_verified && { emailVerified: new Date() }), }; }, @@ -85,6 +81,31 @@ export const authOptions: AuthOptions = { }), ], callbacks: { + // Der signIn Callback kann für komplexere Verknüpfungslogik oder Prüfungen verwendet werden. + // Mit `allowDangerousEmailAccountLinking: true` ist er für das reine Verknüpfen oft nicht nötig. + /* + async signIn({ user, account, profile, email, credentials }) { + if (account?.provider === "keycloak" && profile?.email) { + const existingUser = await prisma.user.findUnique({ + where: { email: profile.email.toLowerCase() }, + include: { accounts: true } // Um zu prüfen, ob bereits ein Keycloak-Konto verknüpft ist + }); + + if (existingUser) { + // Benutzer existiert. Prüfe, ob das Keycloak-Konto bereits verknüpft ist. + const isProviderAccountLinked = existingUser.accounts.some( + acc => acc.provider === account.provider && acc.providerAccountId === account.providerAccountId + ); + if (!isProviderAccountLinked) { + // Hier würde die Verknüpfung durch den Adapter stattfinden, + // wenn allowDangerousEmailAccountLinking true ist. + // Du könntest hier zusätzliche Logik einbauen, z.B. den Benutzer benachrichtigen. + } + } + } + return true; // Erlaube den Anmeldevorgang + }, + */ async jwt({ token, user, account }) { if (account && user) { token.id = user.id;