import 'server-only' import { NextAuthOptions } from "next-auth" import type { User as NextAuthUser } from "next-auth" import CredentialsProvider from "next-auth/providers/credentials" import bcrypt from "bcryptjs" import { db } from "./db" import { clearSessionInvalidation, isSessionInvalidated } from "./service/session" /** 用户查询时需要 include 的关联 */ export const userAuthInclude = { roles: { include: { permissions: true } }, dept: true, } as const /** 从数据库用户对象构建 JWT payload(供密码登录和 IAAA 登录共用) */ export function buildUserJwtPayload(user: { id: string name: string | null status: string | null deptCode: string | null isSuperAdmin: boolean roles: Array<{ name: string; permissions: Array<{ name: string }> }> }) { const roles = user.roles.map((r) => r.name) const permissions = Array.from( new Set(user.roles.flatMap((r) => r.permissions.map((p) => p.name))) ) return { id: user.id, name: user.name, status: user.status, deptCode: user.deptCode, roles, permissions, isSuperAdmin: user.isSuperAdmin, } } export const authOptions: NextAuthOptions = { providers: [ CredentialsProvider({ name: "credentials", credentials: { id: { label: "用户ID", type: "text" }, password: { label: "密码", type: "password" } }, async authorize(credentials, req): Promise { if (!credentials?.id || !credentials?.password) { return null } try { // 查找用户 const user = await db.user.findUnique({ where: { id: credentials.id }, include: userAuthInclude, }) if (!user) { return null } // 验证密码 const isPasswordValid = await bcrypt.compare(credentials.password, user.password) if (!isPasswordValid) { return null } // 更新最近登录时间 await db.user.update({ where: { id: user.id }, data: { lastLoginAt: new Date() } }) // 清除会话失效标记(用户已重新登录,获得最新权限) await clearSessionInvalidation(user.id) return buildUserJwtPayload(user) as any } catch (error) { console.error("Auth error:", error) return null } } }) ], session: { strategy: "jwt", maxAge: 30 * 24 * 60 * 60, // 30 days }, jwt: { maxAge: 30 * 24 * 60 * 60, // 30 days }, pages: { signIn: "/login", }, callbacks: { async jwt({ token, user }) { // 初次登录时,将用户信息保存到JWT token中 if (user) { const u = user as any token = { ...token, id: u.id, name: u.name, status: u.status, deptCode: u.deptCode, roles: u.roles, permissions: u.permissions, isSuperAdmin: u.isSuperAdmin, } } else if (token.id) { // 后续请求:检查会话是否已被标记失效 const invalidated = await isSessionInvalidated(token.id as string) if (invalidated) { token.sessionInvalid = true } } return token }, async session({ session, token }) { // 会话已被标记失效,返回不含用户信息的session if (token.sessionInvalid) { return { expires: session.expires } as any } // 将JWT token中的信息传递给session if (session.user) { const t = token as any session.user = { ...session.user, id: t.id, name: t.name, status: t.status, deptCode: t.deptCode, roles: t.roles, permissions: t.permissions, isSuperAdmin: t.isSuperAdmin, } } return session } }, secret: process.env.NEXTAUTH_SECRET, }