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

NextAuth API路由中unstable_getServerSession与getToken始终返回null求助

Next.js API路由中unstable_getServerSession与getToken始终返回null的问题

在Next.js项目中,尝试将自定义认证切换为NextAuth的unstable_getServerSession,但API端点中unstable_getServerSession和getToken始终返回null,导致请求被判定为未授权。

已按照NextAuth官方「Securing API routes」教程配置:

  • 在getServerSideProps中通过Axios调用内部API端点
  • 请求时携带Bearer token
  • API端点需通过JWT验证用户身份,并从JWT中提取stripe_customer_id获取Stripe订阅信息

当前getServerSideProps中能正常获取到正确的session和token,但API端始终无法获取,JWT由Rails后端生成,相关代码如下:

getServerSideProps函数

export const getServerSideProps: GetServerSideProps = async (context: GetServerSidePropsContext) => {

    // 此处能获取到正确的SESSION(正常工作)
    const session = await unstable_getServerSession(context.req, context.res, authOptions)

    // 此处能获取到正确的TOKEN(正常工作)
    const token = await getToken(context).then(token => {
            return token.token as string; 
        })
    if (!session) {
        return {
            redirect: {
                permanent: false,
                destination: url.accountSignIn().href,
            },
        };
    }
    else {
        // SESSION可用(用于展示用户数据)
        // 调用内部API端点,传递getToken()返回的token
        const stripeSubscriptions = await axios.get(`${process.env.NEXT_PUBLIC_URL}/api/account/subscriptions`, { headers: { 'Authorization': 'Bearer ' + token } }).then(res => {
            console.log('res.data :>> ', res.data);
            const subscriptions = res.data.data as Stripe.Subscription[]
            return subscriptions
        }).catch((err: any) => {
            console.log("Dashboard ERR", err.response.data.message || err); // 始终返回 {error: "Unauthorized", message: "not authorized", data: []}
  
            signOut({ callbackUrl: 'http://localhost:8000/account/login' })
            return err.response.data

        });

        return {
            props: { stripeSubscriptions, session }, // 传递给页面组件的props
        };
    }

API端点 /api/account/subscriptions

import { NextApiRequest, NextApiResponse } from 'next';
import Stripe from 'stripe';

const secret = process.env.NEXTAUTH_SECRET // 已确认正确
import { unstable_getServerSession } from "next-auth/next"
import { authOptions } from "../../api/auth/[...nextauth]"
import { getToken } from "next-auth/jwt"

export default async (req: NextApiRequest, res: NextApiResponse) => {

    // 请求头中确实存在Bearer token: req.headers.authorization: `"Bearer eyJhbGciOiJIUzI1NiJ9......"`
    const session = await unstable_getServerSession(req, res, authOptions)  // 始终返回NULL
    const token = await getToken({ req, secret }) // 始终返回NULL

    if (session) { // 始终为NULL

        return new Promise<void>((resolve) => {
            const customer_id = session.stripe_id as string; // 应从JWT中提取
            const stripe = new Stripe(process.env.STRIPE_API_SECRET!, { apiVersion: '2020-08-27' });

            stripe.subscriptions.list({ customer: customer_id }).then(subscriptions => {
                res.status(200).json({ data: subscriptions });
                return resolve();
            }).catch(err => {
                res.status(500).json({ error: `Stripe Subscriptions error: ${err.message}` });
                return resolve();
            }
            );
        })
    } else {
        // 未登录分支,始终进入这里
        res.status(401).json({ error: 'Unauthorized', message: "not authorized", data: [] });
    }
    res.end()

[...nextauth].ts配置

import jwtDecode from 'jwt-decode';
import NextAuth from 'next-auth';
import type { NextAuthOptions } from 'next-auth';

import ProviderCredentials from 'next-auth/providers/credentials';
import { JWT } from 'next-auth/jwt';

export const authOptions: NextAuthOptions = {
    // 配置认证提供者
    providers: [
        ProviderCredentials({
            id: 'credentials',
            name: 'credentials',
            credentials: {
                username: {
                    label: 'Email',
                    type: 'email',
                    placeholder: 'email@ihrUnternehmen.com',
                },
                password: { label: 'Passwort', type: 'password' },
            },
            async authorize(credentials, req) {
                // 请求Rails API端点获取JWT Token和用户数据
                const url = `${process.env.NEXT_PUBLIC_API_URL}/auth/login`;

                const res = await fetch(url, {
                    method: 'POST',
                    body: JSON.stringify(credentials),
                    headers: {
                        'Content-Type': 'application/json',
                    },
                });
                const user = await res.json();
                // 处理错误信息
                console.log('user authorize :>> ', user);
                if (user && (user.error || user.message)) {
                    throw new Error(user.message || user.error);
                }

                if (res.ok && user) {
                    return user; // 返回用户数据
                }
                // 获取失败返回null
                return null;
            },
        }),
    ],
    session: {
        strategy: 'jwt',
    },
    jwt: {
        secret: process.env.NEXTAUTH_SECRET,
    },
    secret: process.env.NEXTAUTH_SECRET,
    callbacks: {
        // JWT创建或更新时触发
        async jwt(props) {
            var { token, user, account, profile, isNewUser } = props
            console.log('JWT: props :>> ', props);
            // 用户登录时,将Rails返回的token和用户信息存入JWT
            if (user) {
                if (user.token) {
                    var token = {
                        token: user.token,
                        name: user.name,
                        email: user.email,
                        shop: user.shop,
                        stripeId: user.stripe_id,
                        billingAddress: user.billingAddress,
                    };
                }
            }
            return token;
        },

        // 控制用户是否允许登录
        async signIn(props) {
            const { user, account } = props

            console.log('signIn: props :>> ', props);
            return true;
        },

        // 用户登出时触发
        async signOut({ token, session }) {
            console.log('signOUT token :>> ', token);
            console.log('signOUT session :>> ', session);
            return true;
        },
        // 配置客户端可用的session数据
        async session(props) {
            const { session, token, user } = props;
            console.log('session: props :>> ', props);
            // 将JWT中的数据存入session
            if (token) {
                session.user = token;
            }
            return session;
        },

        redirect({ url, baseUrl }) {
            if (url.startsWith(baseUrl)) return url;
            // 允许相对回调URL
            else if (url.startsWith('/')) return new URL(url, baseUrl).toString();
            return baseUrl;
        },
    },

    pages: {
        signIn: 'account/login',
        // signOut: 'account/logout',
        // error: '/auth/error', // 错误码通过query参数传递?error=
        // verifyRequest: '/auth/verify-request', // 用于验证邮箱的页面
        // newUser: '/auth/new-user', // 新用户首次登录跳转页面(不需要可移除)
    },
    // events: {
    //     async signIn(message) {
    //         console.log('user sign in EVENT called ;:>> ', message);
    //     },
    //     async signOut(message) {
    //         console.log('user sign out EVENT called ;:>> ', message);
    //     },
    // },
};
export default NextAuth(authOptions);

内容的提问来源于stack exchange,提问作者Jan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 01:50:25