import { NextRequest, NextResponse } from 'next/server' import { encode } from 'next-auth/jwt' import { db } from '@/server/db' import { authOptions, buildUserJwtPayload, userAuthInclude } from '@/server/auth' import { validateIaaaToken } from '@/server/service/iaaa' import { clearSessionInvalidation } from '@/server/service/session' /** * IAAA 统一认证回调路由 * * IAAA 认证成功后会 302 重定向到此路由,携带 token 参数。 * 本路由验证 token、查找本地用户、生成 JWT session cookie,然后重定向到首页。 */ export async function GET(req: NextRequest) { const token = req.nextUrl.searchParams.get('token') const loginUrl = new URL('/login', req.url) if (!token) { loginUrl.searchParams.set('iaaa_error', 'iaaa_no_token') return NextResponse.redirect(loginUrl) } // 获取客户端真实 IP(反向代理场景取 X-Forwarded-For 第一个值) const remoteAddr = req.headers.get('x-forwarded-for')?.split(',')[0].trim() || req.headers.get('x-real-ip') || '127.0.0.1' // 调用 IAAA 验证 const userInfo = await validateIaaaToken(token, remoteAddr) if (!userInfo) { loginUrl.searchParams.set('iaaa_error', 'iaaa_validate_failed') return NextResponse.redirect(loginUrl) } // 用 identityId(学号/职工号)匹配本地用户 const user = await db.user.findUnique({ where: { id: userInfo.identityId }, include: userAuthInclude, }) if (!user) { console.warn(`[IAAA] 用户 ${userInfo.identityId}(${userInfo.name})在系统中不存在`) loginUrl.searchParams.set('iaaa_error', 'iaaa_user_not_found') return NextResponse.redirect(loginUrl) } // 更新最近登录时间 await db.user.update({ where: { id: user.id }, data: { lastLoginAt: new Date() }, }) // 清除会话失效标记 await clearSessionInvalidation(user.id) // 构建 JWT payload(与密码登录完全一致) const payload = buildUserJwtPayload(user) // 生成 next-auth 兼容的 JWT const secret = authOptions.secret || process.env.NEXTAUTH_SECRET if (!secret) { console.error('[IAAA] NEXTAUTH_SECRET 未配置') loginUrl.searchParams.set('iaaa_error', 'iaaa_server_error') return NextResponse.redirect(loginUrl) } const maxAge = authOptions.session?.maxAge ?? 30 * 24 * 60 * 60 const encodedToken = await encode({ token: { ...payload, sub: payload.id }, secret, maxAge, }) // 设置 session cookie 并重定向到首页 // next-auth 在 HTTPS 环境下使用 __Secure- 前缀 const useSecureCookie = req.nextUrl.protocol === 'https:' const cookieName = useSecureCookie ? '__Secure-next-auth.session-token' : 'next-auth.session-token' const response = NextResponse.redirect(new URL('/', req.url)) response.cookies.set(cookieName, encodedToken, { httpOnly: true, secure: useSecureCookie, sameSite: 'lax', path: '/', maxAge, }) console.info(`[IAAA] 登录成功: ${userInfo.identityId}(${userInfo.name})`) return response }