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)
核心原因
- Token中的用户字段未声明明确类型,TypeScript无法推断具体类型,导致
unknown错误。 - 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

