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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 16:14:56