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

Next.js+Auth.js集成LDAP认证时出现Cannot read 'username'错误

问题分析与解决方案

问题1:Session回调中user未定义导致TypeError: Cannot read properties of undefined (reading 'username')

使用Credentials Provider时,Auth.js默认不会将认证后的用户信息持久化到数据库(未配置适配器时),因此session回调中的user参数会是undefined。这是Credentials Provider的无状态特性导致的,需要通过JWT Token手动传递用户信息。

问题2:改用Token传递后出现TypeScript类型不兼容错误

你的错误信息翻译如下:

类型 '({ session, token }: { session: { user: AdapterUser; } & AdapterSession; user: AdapterUser; } & { session: Session; token: JWT; } & { newSession: any; trigger?: "update" | undefined; }) => Promise<...>' 不能分配给类型 '(params: { session: { user: AdapterUser; } & AdapterSession; user: AdapterUser; } & { session: Session; token: JWT; } & { newSession: any; trigger?: "update" | undefined; }) => Awaitable<...>'.
类型 'Promise<{ user: { username: unknown; displayName: unknown; memberOf: unknown; email: unknown; id: string; emailVerified: Date | null; name?: string | null | undefined; image?: string | null | undefined; mail: string | string[] | Buffer | Buffer[]; }; sessionToken: string; userId: string; expires: Date & string; }>' 不能分配给类型 'Awaitable<Session | DefaultSession>'.
类型 'Promise<{ user: { username: unknown; displayName: unknown; memberOf: unknown; email: unknown; id: string; emailVerified: Date | null; name?: string | null | undefined; image?: string | null | undefined; mail: string | string[] | Buffer | Buffer[]; }; sessionToken: string; userId: string; expires: Date & string; }>' 不能分配给类型 'PromiseLike<Session | DefaultSession>'.
属性 'then' 的类型不兼容。
类型 '<TResult1 = { user: { username: unknown; displayName: unknown; memberOf: unknown; email: unknown; id: string; emailVerified: Date | null; name?: string | null | undefined; image?: string | null | undefined; mail: string | string[] | Buffer | Buffer[]; }; sessionToken: string; userId: string; expires: Date & string; ...' 不能分配给类型 '<TResult1 = Session | DefaultSession, TResult2 = never>(onfulfilled?: ((value: Session | DefaultSession) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<...>) | ... 1 more ... | undefined) => PromiseLike<...>'.
参数 'onfulfilled' 和 'onfulfilled' 的类型不兼容。
参数 'value' 和 'value' 的类型不兼容。
类型 '{ user: { username: unknown; displayName: unknown; memberOf: unknown; email: unknown; id: string; emailVerified: Date | null; name?: string | null | undefined; image?: string | null | undefined; mail: string | string[] | Buffer | Buffer[]; }; sessionToken: string; userId: string; expires: Date & string; }' 不能分配给类型 'Session | DefaultSession'.
类型 '{ user: { username: unknown; displayName: unknown; memberOf: unknown; email: unknown; id: string; emailVerified: Date | null; name?: string | null | undefined; image?: string | null | undefined; mail: string | string[] | Buffer | Buffer[]; }; sessionToken: string; userId: string; expires: Date & string; }' 不能分配给类型 'DefaultSession'.
这些类型之间 'user.email' 的类型不兼容。
类型 'unknown' 不能分配给类型 'string | null | undefined'.ts(2322)

核心原因

  1. Token中的用户字段未声明明确类型,TypeScript无法推断具体类型,导致unknown错误。
  2. Session回调返回的结构不符合Auth.js的Session/DefaultSession类型要求,尤其是user.email必须为string | null | undefined,而你的代码中为unknown。

解决方案

步骤1:扩展Auth.js类型定义

创建types/next-auth.d.ts文件,扩展JWT和Session的类型,匹配LDAP返回的用户字段:

import NextAuth from "next-auth";

declare module "next-auth" {
  interface Session {
    user: {
      id: string;
      username: string;
      displayName: string;
      memberOf: string[];
      email: string | null;
      emailVerified: Date | null;
      mail?: string;
    };
  }

  interface User {
    id: string;
    username: string;
    displayName: string;
    memberOf: string[];
    email: string | null;
    emailVerified: Date | null;
    mail?: string;
  }
}

declare module "next-auth/jwt" {
  interface JWT {
    id: string;
    username: string;
    displayName: string;
    memberOf: string[];
    email: string | null;
    emailVerified: Date | null;
    mail?: string;
  }
}

步骤2:修改auth.ts配置

确保authorize回调返回明确类型的用户对象,jwt回调存入用户信息,session回调从Token提取并返回符合类型的Session:

import NextAuth from "next-auth";
import Credentials from "next-auth/providers/credentials";
import { verifyLDAPCredentials } from "@/utils/ldap";

export const { handlers, auth, signIn, signOut } = NextAuth({
  providers: [
    Credentials({
      credentials: {
        username: { label: "Username", type: "text" },
        password: { label: "Password", type: "password" },
      },
      async authorize(credentials) {
        if (!credentials?.username || !credentials?.password) return null;

        const user = await verifyLDAPCredentials(
          credentials.username,
          credentials.password
        );

        if (!user) return null;

        return {
          id: user.id,
          username: user.username,
          displayName: user.displayName,
          memberOf: user.memberOf,
          email: user.email || null,
          emailVerified: user.emailVerified || null,
          mail: user.mail,
        };
      },
    }),
  ],
  session: { strategy: "jwt" }, // Credentials Provider必须用JWT策略
  callbacks: {
    async jwt({ token, user }) {
      if (user) {
        token.id = user.id;
        token.username = user.username;
        token.displayName = user.displayName;
        token.memberOf = user.memberOf;
        token.email = user.email;
        token.emailVerified = user.emailVerified;
        token.mail = user.mail;
      }
      return token;
    },
    async session({ session, token }) {
      if (token) {
        session.user = {
          id: token.id,
          username: token.username,
          displayName: token.displayName,
          memberOf: token.memberOf,
          email: token.email,
          emailVerified: token.emailVerified,
          mail: token.mail,
        };
      }
      return session;
    },
  },
  pages: {
    signIn: "/login",
  },
});

步骤3:明确LDAP验证函数返回类型

确保verifyLDAPCredentials返回匹配扩展类型的用户对象:

// utils/ldap.ts
export async function verifyLDAPCredentials(username: string, password: string) {
  const ldapUser = await yourLDAPClient.authenticate(username, password);
  
  if (!ldapUser) return null;

  return {
    id: ldapUser.id,
    username: ldapUser.sAMAccountName,
    displayName: ldapUser.displayName,
    memberOf: ldapUser.memberOf,
    email: ldapUser.mail || null,
    emailVerified: null,
    mail: ldapUser.mail,
  };
}

关键注意事项

  • 必须设置session: { strategy: "jwt" },Credentials Provider无状态,无法依赖数据库持久化用户信息。
  • 扩展类型时,所有字段类型需与实际返回值严格匹配,避免unknown类型。
  • authorize回调只能返回User类型对象或null,不能返回undefined。

内容的提问来源于stack exchange,提问作者moth

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 16:42:01