Next.js 13中getServerSession返回null问题求助(Google OAuth)
Next.js 13 Google OAuth登录:
getServerSession始终返回null的问题 问题描述
配置Next.js 13应用的Google OAuth登录时,跳转Google登录页流程正常,但登录后跳转到/dashboard时,getServerSession(authOptions)始终返回null,导致页面因会话检查返回404。已尝试设置NEXTAUTH_URL、NEXTAUTH_SECRET(含NEXTAUTH_URL_INTERNAL),问题仍存在。
环境变量配置
NEXTAUTH_SECRET=randomletters NEXTAUTH_URL=http://localhost:3000
相关代码
(dashboard)/dashboard/page.tsx
import { FC } from "react" import ApiDashboard from "@/components/ApiDashboard" import RequestApiKey from "@/components/RequestApiKey" import { db } from "@/lib/db" import { getServerSession } from "next-auth" import type { Metadata } from "next" import { notFound } from "next/navigation" import { authOptions } from "@/pages/api/auth/[...nextauth]" export const metadata: Metadata = { title: "Similarity API | Dashboard", description: "Free and open-source text smilarity API", } const page = async () => { const user = await getServerSession(authOptions) //<-- 返回null if (!user) return notFound() //<-- 因此返回404 const apiKey = await db.apiKey.findFirst({ where: { userId: user.user.id, enabled: true }, }) return ( <div className="mx-auto mt-16 max-w-7xl"> {user ? <ApiDashboard /> : <RequestApiKey />} </div> ) } export default page
Navbar组件代码
import { getServerSession } from "next-auth/next" import Link from "next/link" import { FC } from "react" import { buttonVariants } from "@/ui/Button" import SignInButton from "@/components/ui/SignInButton" import SignOutButton from "@/components/ui/SignOutButton" import ThemeToggle from "./ThemeToggle" import { authOptions } from "@/pages/api/auth/[...nextauth]" const Navbar = async () => { const session = await getServerSession(authOptions) //<-- 同样返回null return ( /* 组件内容 */ ) }
[...nextauth].ts代码
import NextAuth from "next-auth" import { db } from "@/lib/db" import { PrismaAdapter } from "@next-auth/prisma-adapter" import { NextAuthOptions } from "next-auth" import GoogleProvider from "next-auth/providers/google" function getGoogleCredentials(): { clientId: string; clientSecret: string } { const clientId = process.env.GOOGLE_CLIENT_ID const clientSecret = process.env.GOOGLE_CLIENT_SECRET if (!clientId || clientId.length === 0) { throw new Error("Missing GOOGLE_CLIENT_ID") } if (!clientSecret || clientSecret.length === 0) { throw new Error("Missing GOOGLE_CLIENT_SECRET") } return { clientId, clientSecret } } export const authOptions: NextAuthOptions = { adapter: PrismaAdapter(db), session: { strategy: "jwt", }, pages: { signIn: "/login", }, providers: [ GoogleProvider({ clientId: getGoogleCredentials().clientId, clientSecret: getGoogleCredentials().clientSecret, }), ], callbacks: { async session({ token, session }) { if (token) { session.user.id = token.id session.user.name = token.name session.user.email = token.email session.user.image = token.picture } return session }, async jwt({ token, user }) { const dbUser = await db.user.findFirst({ where: { email: token.email, }, }) if (!dbUser) { token.id = user!.id return token } return { id: dbUser.id, name: dbUser.name, email: dbUser.email, picture: dbUser.image, } }, async redirect() { return "/dashboard" }, }, } export default NextAuth(authOptions)
解决方案
1. 确认NextAuth版本兼容性
确保使用next-auth v4.20.0及以上版本,该版本完善了对Next.js 13 App Router的支持。升级命令:
npm update next-auth # 或 yarn upgrade next-auth
2. 抽离authOptions到独立文件
将authOptions从pages/api/auth/[...nextauth]中抽离到@/lib/auth.ts,避免跨目录导入导致的上下文问题:
// @/lib/auth.ts import { db } from "@/lib/db" import { PrismaAdapter } from "@next-auth/prisma-adapter" import { NextAuthOptions } from "next-auth" import GoogleProvider from "next-auth/providers/google" function getGoogleCredentials(): { clientId: string; clientSecret: string } { const clientId = process.env.GOOGLE_CLIENT_ID const clientSecret = process.env.GOOGLE_CLIENT_SECRET if (!clientId || clientId.length === 0) { throw new Error("Missing GOOGLE_CLIENT_ID") } if (!clientSecret || clientSecret.length === 0) { throw new Error("Missing GOOGLE_CLIENT_SECRET") } return { clientId, clientSecret } } export const authOptions: NextAuthOptions = { adapter: PrismaAdapter(db), session: { strategy: "jwt", }, pages: { signIn: "/login", }, providers: [ GoogleProvider({ clientId: getGoogleCredentials().clientId, clientSecret: getGoogleCredentials().clientSecret, }), ], callbacks: { async session({ token, session }) { if (token) { session.user.id = token.id session.user.name = token.name session.user.email = token.email session.user.image = token.picture } return session }, async jwt({ token, user }) { if (user) token.id = user.id const dbUser = await db.user.findUnique({ where: { email: token.email }, }) if (!dbUser) return token return { id: dbUser.id, name: dbUser.name, email: dbUser.email, picture: dbUser.image, } }, async redirect() { return "/dashboard" }, }, cookies: { sessionToken: { name: `next-auth.session-token`, options: { httpOnly: true, sameSite: "lax", path: "/", secure: process.env.NODE_ENV === "production", }, }, }, }
然后在pages/api/auth/[...nextauth].ts和所有App Router组件中,统一从@/lib/auth导入authOptions。
3. 修正Cookie配置
在authOptions中添加Cookie配置,确保本地开发时不强制HTTPS,避免Cookie无法写入:
// 已包含在上面的@/lib/auth.ts中 cookies: { sessionToken: { name: `next-auth.session-token`, options: { httpOnly: true, sameSite: "lax", path: "/", secure: process.env.NODE_ENV === "production", // 仅生产环境启用HTTPS }, }, },
4. 优化JWT回调逻辑
改用findUnique查询用户(比findFirst更可靠),并优先处理首次登录的user参数:
// 已包含在上面的@/lib/auth.ts中 async jwt({ token, user }) { // 首次登录时直接设置token.id if (user) token.id = user.id const dbUser = await db.user.findUnique({ where: { email: token.email }, }) if (!dbUser) return token return { id: dbUser.id, name: dbUser.name, email: dbUser.email, picture: dbUser.image, } },
5. 重启服务并清除缓存
修改配置后必须重启Next.js开发服务器,同时清除浏览器Cookie和缓存,避免旧会话残留。
内容的提问来源于stack exchange,提问作者Edris Elbow
相关产品推荐
相关产品推荐

