Next.js 14 + Next Auth 5 Beta JWT会话类型错误求助
Next.js 14 + NextAuth 5 Beta 登录触发[auth][error][JWTSessionError]错误解决方案
问题描述
- 技术栈:Next.js 14 + NextAuth 5 Beta(TypeScript)
- 登录时触发
[auth][error][JWTSessionError],具体错误为TypeError: Cannot read properties of undefined (reading 'username') - 参考的非TypeScript版本可正常运行,但项目必须使用TS开发
- 已通过控制台确认:用户可被成功查询,
username和img已附加到JWT token中,但仍报错
错误截图

原auth.ts代码
import "next-auth"; import CredentialsProvider from "next-auth/providers/credentials"; import { connectDb } from "./lib/utils"; import { User } from "./lib/models"; import bcrypt from "bcrypt"; import { UserType } from "./lib/types"; import NextAuth, { NextAuthConfig } from "next-auth"; declare module "next-auth" { interface Session { user: UserType; } } declare module "@auth/core/types" { interface Session { user: UserType; } } declare module "next-auth" { interface User { username: string; img: string; } } const login = async (credentials: Partial<Record<string, unknown>>) => { try { connectDb(); const user = await User.findOne({ username: credentials.username }); if (!user) throw new Error("Invalid credentials"); const isPasswordCorrect = await bcrypt.compare( credentials.password as string, user.password ); if (!isPasswordCorrect) throw new Error("Invalid credentials"); return user; } catch (error) { console.log(error); throw new Error("Login failed"); } }; export const authConfig: NextAuthConfig = { providers: [ CredentialsProvider({ async authorize(credentials) { try { const user = await login(credentials); return user; } catch (error) { return null; } }, }), ], pages: { signIn: "/login", }, callbacks: { authorized({ auth, request }) { const isLoggedIn = auth?.user; const isOnDashboard = request.nextUrl.pathname.startsWith("/dashboard"); if (isOnDashboard) { if (isLoggedIn) return true; return false; } else if (isLoggedIn) { return Response.redirect(new URL("/dashboard", request.nextUrl)); } return true; }, async session({ session, user }) { if (session.user) { session.user.username = user.username; session.user.img = user.img; } return session; }, async jwt({ token, user }) { if (user) { token.username = user.username || "someUsername"; token.img = user.img || ""; } return token; }, }, }; export const { signIn, signOut, auth } = NextAuth(authConfig);
问题根源
NextAuth 5的session回调中,仅首次登录时user参数存在,后续请求会话从JWT token恢复时,user参数会变为undefined。原代码直接读取user.username,导致后续请求抛出未定义错误。
修复方案
1. 调整session回调,从token读取数据
修改callbacks.session函数,解构token参数并从中获取自定义字段:
async session({ session, token }) { if (session.user && token) { session.user.username = token.username as string; session.user.img = token.img as string; } return session; }
2. 扩展JWT类型声明
添加@auth/core/jwt的类型扩展,让TypeScript识别token中的自定义字段:
declare module "@auth/core/jwt" { interface JWT { username?: string; img?: string; } }
3. 转换Mongoose文档为普通对象
在login函数中,将Mongoose返回的文档对象转为普通JS对象,避免类型不兼容:
return user.toObject();
4. 验证UserType类型定义
确保./lib/types中的UserType包含username和img字段,示例:
export type UserType = { id: string; username: string; img: string; // 其他必要字段... };
修复后的完整auth.ts代码
import "next-auth"; import CredentialsProvider from "next-auth/providers/credentials"; import { connectDb } from "./lib/utils"; import { User } from "./lib/models"; import bcrypt from "bcrypt"; import { UserType } from "./lib/types"; import NextAuth, { NextAuthConfig } from "next-auth"; declare module "next-auth" { interface Session { user: UserType; } } declare module "@auth/core/types" { interface Session { user: UserType; } } declare module "next-auth" { interface User { username: string; img: string; } } declare module "@auth/core/jwt" { interface JWT { username?: string; img?: string; } } const login = async (credentials: Partial<Record<string, unknown>>) => { try { connectDb(); const user = await User.findOne({ username: credentials.username }); if (!user) throw new Error("Invalid credentials"); const isPasswordCorrect = await bcrypt.compare( credentials.password as string, user.password ); if (!isPasswordCorrect) throw new Error("Invalid credentials"); return user.toObject(); } catch (error) { console.log(error); throw new Error("Login failed"); } }; export const authConfig: NextAuthConfig = { providers: [ CredentialsProvider({ async authorize(credentials) { try { const user = await login(credentials); return user; } catch (error) { return null; } }, }), ], pages: { signIn: "/login", }, callbacks: { authorized({ auth, request }) { const isLoggedIn = auth?.user; const isOnDashboard = request.nextUrl.pathname.startsWith("/dashboard"); if (isOnDashboard) { if (isLoggedIn) return true; return false; } else if (isLoggedIn) { return Response.redirect(new URL("/dashboard", request.nextUrl)); } return true; }, async session({ session, token }) { if (session.user && token) { session.user.username = token.username as string; session.user.img = token.img as string; } return session; }, async jwt({ token, user }) { if (user) { token.username = user.username || "someUsername"; token.img = user.img || ""; } return token; }, }, }; export const { signIn, signOut, auth } = NextAuth(authConfig);
内容的提问来源于stack exchange,提问作者CzechCoder
相关产品推荐
相关产品推荐

