NextAuth getServerSession在API路由中无法返回完整用户字段
问题:API路由中getServerSession无法获取自定义session字段
我在编写需要验证用户登录状态和管理员权限的API路由时遇到问题:调用getServerSession方法返回的session对象中,自定义字段(如role、id)为undefined,但在getServerSideProps中使用同一个方法却能正常获取完整的session内容。
代码示例
[..nextauth].ts
import type { NextAuthOptions, Session } from 'next-auth'; import NextAuth from 'next-auth'; import type { JWT } from 'next-auth/jwt'; import CredentialsProvider from 'next-auth/providers/credentials'; import { verifyPassword } from '@/lib/auth'; import { connectToDatabase, initUserTable } from '@/lib/db'; import User from '@/lib/dbmodels/user'; export const authOptions: NextAuthOptions = { secret: process.env.NEXTAUTH_SECRET, session: { strategy: 'jwt', }, providers: [ CredentialsProvider({ name: 'Credentials', credentials: { email: { label: 'Email', type: 'text' }, password: { label: 'Password', type: 'password' }, }, async authorize(credentials) { if (!credentials?.email || !credentials?.password) { throw new Error('Email-ul sau parola sunt greșite'); } const db = await connectToDatabase(); await initUserTable(db); const user = await User.findOne({ where: { email: credentials?.email }, }); if (!user) { db.close(); throw new Error('Acest utilizator nu există'); } const isValid = await verifyPassword( credentials?.password, user.password ); if (!isValid) { db.close(); throw new Error('Email-ul sau parola sunt greșite'); } db.close(); return user.dataValues; }, }), ], callbacks: { async jwt({ user, token }) { const newToken = { ...token }; if (user?.role) { newToken.role = user.role; } if (user?.id) { newToken.id = user.id; } return newToken; }, async session({ session, token }: { session: Session; token: JWT }) { const newSession = { ...session }; newSession.user.role = token.role as 'user' | 'admin'; newSession.user.id = token.id as string; delete newSession.user.image; return newSession; }, }, }; export default NextAuth(authOptions);
API路由
async function handler(req: RegisterNextApiRequest, res: NextApiResponse) { if (req.method !== 'POST') { return res.status(405).json({ error: 'Method not supported' }); } const { name, email, password, role = 'user' } = req.body; const emptyData = name == null || email == null || password == null; const session = await getServerSession(req, res, authOptions); const user = session?.user; if (!user || !user?.email) { return res.status(401).json({ error: 'You must be logged in.' }); } if (user?.role !== 'admin') { return res.status(401).json({ error: 'Only admins can create users!' }); } // Rest of the code }
类型定义文件
import type { DefaultSession } from 'next-auth'; declare module 'next-auth' { interface Session { user: User & DefaultSession['user']; } interface User { id: string; name: string; email: string; role?: 'user' | 'admin'; } }
解决方案
方案1:修正session回调的字段赋值方式
原session回调中直接给newSession.user.role和newSession.user.id赋值,可能因对象属性扩展问题导致自定义字段在API路由的session中丢失。改为合并对象的方式确保字段被正确添加:
修改[..nextauth].ts中的session回调:
async session({ session, token }: { session: Session; token: JWT }) { const newSession = { ...session }; // 合并自定义字段到user对象中 newSession.user = { ...newSession.user, role: token.role as 'user' | 'admin', id: token.id as string }; delete newSession.user.image; return newSession; }
方案2:直接获取JWT令牌(更可靠)
由于你的session策略是jwt,可以绕过getServerSession,直接用getToken方法从请求中解析JWT令牌,直接获取自定义字段:
修改API路由代码:
import { getToken } from 'next-auth/jwt'; // 导入getToken async function handler(req: RegisterNextApiRequest, res: NextApiResponse) { if (req.method !== 'POST') { return res.status(405).json({ error: 'Method not supported' }); } const { name, email, password, role = 'user' } = req.body; const emptyData = name == null || email == null || password == null; // 直接获取JWT令牌 const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET }); if (!token || !token.email) { return res.status(401).json({ error: 'You must be logged in.' }); } if (token.role !== 'admin') { return res.status(401).json({ error: 'Only admins can create users!' }); } // Rest of the code }
这两种方案都能解决API路由中无法获取自定义session字段的问题,方案2更直接,因为它直接读取存储在JWT中的数据,避免了session对象序列化可能带来的问题。
内容的提问来源于stack exchange,提问作者vladcristianmarin
相关产品推荐
相关产品推荐

