You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 08:42:39