import NextAuth, { type NextAuthOptions } from 'next-auth' import CredentialsProvider from 'next-auth/providers/credentials' import KeycloakProvider from 'next-auth/providers/keycloak' import { PrismaClient } from '@prisma/client' import bcrypt from 'bcrypt' const prisma = new PrismaClient() export const authOptions: NextAuthOptions = { providers: [ CredentialsProvider({ name: 'Credentials', credentials: { email: { label: 'Email', type: 'email' }, password: { label: 'Password', type: 'password' }, }, async authorize(credentials) { if (!credentials?.email || !credentials?.password) { return null } const user = await prisma.user.findUnique({ where: { email: credentials.email }, }) if (!user) { return null } if (!user.password) { return null } const isValid = await bcrypt.compare(credentials.password, user.password) if (!isValid) { return null } return { id: user.id, email: user.email, name: user.name, role: user.role, username: user.username!, } }, }), ...(process.env.OIDC_CLIENT_ID && process.env.OIDC_CLIENT_SECRET && process.env.OIDC_ISSUER ? [ { id: 'oidc', name: process.env.OIDC_PROVIDER_NAME || 'PocketID', type: 'oauth' as const, // Use const assertion here wellKnown: `${process.env.OIDC_ISSUER}/.well-known/openid-configuration`, authorization: { params: { scope: 'openid email profile' } }, clientId: process.env.OIDC_CLIENT_ID, clientSecret: process.env.OIDC_CLIENT_SECRET, idToken: true, checks: ['pkce', 'state'] as any, profile(profile: any) { return { id: profile.sub, name: profile.name || profile.preferred_username || profile.email, email: profile.email, image: profile.picture, role: mapPocketIDRoleToAppRole(profile), } }, } as any ] : [] ), ], session: { strategy: 'jwt', }, callbacks: { async jwt({ token, user }) { if (user) { token.id = user.id token.role = user.role token.username = user.username } return token }, async session({ session, token }) { if (session.user) { session.user.id = token.id as string session.user.role = token.role as "COUPLE" | "PLANNER" | "GUEST" session.user.username = token.username as string } return session }, }, secret: process.env.NEXTAUTH_SECRET, } function mapPocketIDRoleToAppRole(profile: any): "COUPLE" | "PLANNER" | "GUEST" { const roles = profile.roles || profile.groups || profile.realm_access?.roles || profile.resource_access?.[process.env.OIDC_CLIENT_ID || '']?.roles || [] if (roles.includes('admin') || roles.includes('planner')) { return 'PLANNER' } else if (roles.includes('couple') || roles.includes('user')) { return 'COUPLE' } else { return 'GUEST' } } const handler = NextAuth(authOptions) export { handler as GET, handler as POST }