Next.js 13 NextAuth中间件异常:已登录用户仍需重新登录
已登录用户访问受保护路由仍被要求重新登录(NextAuth中间件+Prisma适配器)
我用NextAuth中间件保护了/api/:path*和/new路由,要求未登录用户必须登录,但已登录用户访问/new时还是会被强制跳转到登录页,排查后发现问题出在/api/:path*的匹配规则上。使用Prisma作为NextAuth的适配器,相关代码如下:
中间件文件
export {default} from "next-auth/middleware"; export const config = { matcher: ["/api/:path*", "/new"] }
NextAuth配置文件(/api/auth/[...nextauth]/options.ts)
// /api/auth/[...nextauth]/options.ts import { prisma } from "@/lib/database"; import { PrismaAdapter } from "@next-auth/prisma-adapter"; import { type NextAuthOptions } from "next-auth"; import GithubProvider from "next-auth/providers/github"; import GoogleProvider from "next-auth/providers/google"; export const authOptions: NextAuthOptions = { adapter: PrismaAdapter(prisma), secret: process.env.NEXTAUTH_SECRET, session: { maxAge: 30 * 60, }, debug: process.env.NODE_ENV === "development" ? true : false, pages: { signIn: "/auth/login", }, providers: [ GithubProvider({ clientId: process.env.GITHUB_CLIENT_ID as string, clientSecret: process.env.GITHUB_CLIENT_SECRET as string, }), GoogleProvider({ clientId: process.env.GOOGLE_CLIENT_ID as string, clientSecret: process.env.GOOGLE_CLIENT_SECRET as string, }), ], };
路由文件(/api/auth/[...nextauth]/route.ts)
import NextAuth from "next-auth/next"; import { authOptions } from "./options"; const handler = NextAuth(authOptions); export { handler as GET, handler as POST };
Prisma Schema文件
// This is your Prisma schema file, // learn more about it in the docs: https://pris.ly/d/prisma-schema generator client { provider = "prisma-client-js" } datasource db { provider = "mysql" url = env("DATABASE_URL") relationMode = "prisma" } model Todo { id String @id @default(cuid()) todo_name String todo_description String? completed Boolean? @default(false) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user User? @relation(fields: [userId], references: [id]) userId String? @@index([userId]) } model Account { id String @id @default(cuid()) userId String type String provider String providerAccountId String refresh_token String? @db.Text access_token String? @db.Text expires_at Int? token_type String? scope String? id_token String? @db.Text session_state String? user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([provider, providerAccountId]) @@index([userId]) } model Session { id String @id @default(cuid()) sessionToken String @unique userId String expires DateTime user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([userId]) } model User { id String @id @default(cuid()) name String? email String? @unique emailVerified DateTime? image String? accounts Account[] sessions Session[] todos Todo[] } model VerificationToken { identifier String token String @unique expires DateTime @@unique([identifier, token]) }
解决方案
问题根源在于/api/:path*的匹配规则把NextAuth自身的认证API路由(比如/api/auth/session)也包含进去了,中间件拦截这些请求后,NextAuth无法正常读取用户会话,导致误判用户未登录。
你需要修改中间件的匹配规则,排除NextAuth的认证路由,有两种写法:
写法一:使用excludedRoutes
export {default} from "next-auth/middleware"; export const config = { matcher: ["/api/:path*", "/new"], excludedRoutes: ["/api/auth/:path*"] }
写法二:使用否定匹配语法
export {default} from "next-auth/middleware"; export const config = { matcher: ["/((?!api/auth).*)", "/new"] }
如果你的API路由只有部分需要保护,更推荐精准指定需要保护的路径(比如/api/todos/:path*),避免大范围匹配带来的意外问题。
原因说明
当中间件匹配/api/:path*时,NextAuth用来验证会话的/api/auth/session请求会被拦截。中间件本身需要依赖这个接口获取用户会话状态,但此时接口被自己拦截,形成循环,导致无法正确识别已登录用户,最终强制跳转到登录页。
内容的提问来源于stack exchange,提问作者GN Vageesh
相关产品推荐
相关产品推荐

