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
相关产品推荐
相关产品推荐

