Auth.js中auth()方法返回空user对象的原因及会话关联问题
问题
在Next.js项目中用Auth.js的Credentials Provider实现登录,功能能正常运行,但登录后调用auth()返回的session里user是空对象:
// 未登录 const session = await auth(); console.log(session) // null // 登录后 const session = await auth(); console.log(session) // { user: {}, expires: '2024-07-28T14:03:13.208Z' }
调用外部登录API返回的数据结构如下:
{ result: { status_code: 0, status: 'Ok', message: 'Sign in' }, payload: { data: { auth: [Object], // Bearer令牌 refresh_token: [Object], // 刷新令牌 user: [Object], // 用户信息 project: [Object] // 用户关联项目信息 } } }
需要明确:
- 为什么session里的
user是空对象?正确形态应该是什么样? - API返回的auth、refresh_token、user、project字段和Auth.js会话的关联关系,以及如何绑定?
- API返回的user与session里的user对象的对应方式。
当前使用的auth.ts代码:
import NextAuth from "next-auth"; import CredentialsProvider from "next-auth/providers/credentials"; export const { handlers: { GET, POST }, auth, signIn, signOut } = NextAuth({ session: { strategy: "jwt", }, providers: [ CredentialsProvider({ async authorize(credentials){ try { const login = await fetch(`https://some_project/login`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(credentials), }); const user = await login.json(); console.log(user); return user; } catch (error) { throw new Error("") } } }) ] })
解答
1. session.user为空的原因
你在authorize回调里直接返回了整个API响应对象,但Auth.js默认会从返回值中提取id、name、email这类标准字段来构建session的user对象。而你的用户信息嵌套在payload.data.user里,外层对象没有符合要求的标准字段,导致Auth.js无法提取有效数据,最终生成空的user对象。
2. 正确的代码修改方案
因为使用的是jwt会话策略,需要通过jwt和session两个回调定制token和session内容,把API返回的有效数据关联进去:
import NextAuth from "next-auth"; import CredentialsProvider from "next-auth/providers/credentials"; export const { handlers: { GET, POST }, auth, signIn, signOut } = NextAuth({ session: { strategy: "jwt", }, providers: [ CredentialsProvider({ async authorize(credentials){ try { const loginRes = await fetch(`https://some_project/login`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(credentials), }); if (!loginRes.ok) throw new Error("登录失败"); const apiData = await loginRes.json(); // 提取API返回的核心数据(根据实际字段名调整) const { auth, refresh_token, user, project } = apiData.payload.data; // 返回需要存入JWT的关键数据,该返回值会传给jwt回调 return { accessToken: auth.token, // Bearer令牌内容 refreshToken: refresh_token.token, // 刷新令牌内容 userInfo: user, // 完整用户信息 projectInfo: project // 用户关联项目信息 }; } catch (error) { throw new Error("登录失败,请检查账号密码"); } } }) ], callbacks: { // 定制JWT存储的内容 async jwt({ token, user }) { // user仅在首次登录时存在,来自authorize的返回值 if (user) { token.accessToken = user.accessToken; token.refreshToken = user.refreshToken; token.user = user.userInfo; token.project = user.projectInfo; } return token; }, // 定制返回给前端的session内容 async session({ session, token }) { // 将JWT中的自定义数据同步到session session.user = token.user as any; session.project = token.project as any; session.accessToken = token.accessToken as string; // 注意:refreshToken不要暴露给前端,仅存于JWT中 return session; } } })
3. API字段与会话的关联说明
- auth(Bearer令牌):存入JWT的
accessToken字段,后续请求外部API时用该令牌做身份认证。 - refresh_token:存入JWT的
refreshToken字段,用于令牌过期时调用API刷新accessToken(需额外实现刷新逻辑)。 - user:直接赋值给session的
user对象,前端调用auth()就能拿到完整的用户信息,比如session.user.id、session.user.username等(根据API返回的user结构调整)。 - project:可存入session的
project字段,方便前端直接获取用户关联的项目信息。
4. 修改后的session形态
登录后调用auth()会返回类似如下结构:
{ user: { id: 123, username: "xxx", email: "xxx@xxx.com", ... }, // API返回的完整user内容 project: { id: 456, name: "项目名", ... }, // API返回的完整project内容 accessToken: "Bearer xxxxxx", // Bearer令牌 expires: "2024-07-28T14:03:13.208Z" }
内容的提问来源于stack exchange,提问作者Jim
相关产品推荐
相关产品推荐

