diff --git a/src/pages/api/auth/[...nextauth].ts b/src/pages/api/auth/[...nextauth].ts index b8f1228..82f7cc6 100644 --- a/src/pages/api/auth/[...nextauth].ts +++ b/src/pages/api/auth/[...nextauth].ts @@ -1,6 +1,6 @@ // Datei: pages/api/auth/[...nextauth].ts -import NextAuth, { AuthOptions } from "next-auth"; +import NextAuth, {AuthOptions, TokenSet} from "next-auth"; import CredentialsProvider from "next-auth/providers/credentials"; import KeycloakProvider, { KeycloakProfile } from "next-auth/providers/keycloak"; import { PrismaAdapter } from "@next-auth/prisma-adapter"; @@ -14,13 +14,22 @@ export const authOptions: AuthOptions = { session: { strategy: "jwt", }, + // Debug-Modus für ausführlichere Logs von NextAuth.js aktivieren + debug: true, // process.env.NODE_ENV === 'development', providers: [ KeycloakProvider({ 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(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')) { @@ -29,21 +38,26 @@ export const authOptions: AuthOptions = { 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 { + const emailFromProvider = profile.email?.toLowerCase(); // E-Mail normalisieren (zu Kleinbuchstaben) + + 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: profile.email, + email: emailFromProvider, // Normalisierte E-Mail verwenden image: profile.picture, role: userRole, 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() }), }; + + console.log("[NextAuth.js] User object to be processed by adapter/callbacks:", JSON.stringify(userToReturn, null, 2)); + return userToReturn; }, }), CredentialsProvider({ @@ -81,31 +95,6 @@ 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;