Next-Auth Credentials认证实现:对接已返回JWT令牌的Rails API
Next-Auth CredentialsProvider 对接外部返回JWT的接口配置方案
问题根源
你当前拿到的session_token并非Rails返回的JWT,是Next-Auth默认生成机制导致的:
authorize方法返回的用户对象只会在首次触发jwt回调时作为入参传入,不会自动写入Next-Auth生成的JWT载荷- Next-Auth默认生成JWT时仅会自动添加签发时间
iat、过期时间exp两个字段,自定义的token内容会被默认丢弃 - 你看到的30天有效期是Next-Auth的默认配置,和Rails返回的JWT有效期无关
配置说明
你不需要在authorize里重新组装令牌,只需要额外配置jwt和session两个回调,把Rails返回的JWT及相关信息同步到Next-Auth的会话体系中即可。是否需要解码Rails返回的JWT完全看你的使用需求:
- 如果你只需要透传JWT调用Rails接口,直接存原始JWT字符串即可
- 如果你需要在前端/Next.js服务端直接使用用户ID、角色等信息,可以提前解码后存入JWT载荷,避免后续重复解码
依赖安装(如需解码JWT)
如果需要解析Rails返回的JWT内容,推荐使用适配Edge环境的jose库:
npm install jose
完整[...nextauth].js配置
import NextAuth from "next-auth"; import CredentialsProvider from "next-auth/providers/credentials"; import { decodeJwt } from "jose"; // 不需要解码可删除该行 export default NextAuth({ // 明确指定session使用JWT策略,CredentialsProvider默认也会采用该策略 session: { strategy: "jwt", }, providers: [ CredentialsProvider({ name: 'Email', credentials: { email: { label: "Email", type: "email", placeholder: "meine.email@domain.com" }, password: { label: "Passwort", type: "password" } }, async authorize(credentials) { const url = `${process.env.NEXT_PUBLIC_API_URL}/auth/login`; const res = await fetch(url, { method: 'POST', body: JSON.stringify(credentials), headers: { "Content-Type": "application/json" } }) const user = await res.json() if (res.ok && user?.token) { // 不需要解码可直接返回 { rawToken: user.token } const decodedToken = decodeJwt(user.token); return { rawToken: user.token, userId: decodedToken.user_id, roles: decodedToken.roles, exp: decodedToken.exp, }; } return null } }) // 后续要接入Twitter等第三方Provider直接在该数组中添加即可 ], callbacks: { // JWT回调:把自定义信息存入Next-Auth的JWT载荷 async jwt({ token, user }) { // 仅首次登录/授权时user参数有值,后续刷新JWT时仅传入token参数 if (user) { token.rawToken = user.rawToken; token.userId = user.userId; // 不需要解码可删除该行 token.roles = user.roles; // 不需要解码可删除该行 token.exp = user.exp; // 同步Rails JWT的过期时间,不需要可删除该行使用默认30天有效期 } return token; }, // Session回调:把JWT里的信息同步到session对象,供前端useSession调用 async session({ session, token }) { session.rawToken = token.rawToken; session.user.id = token.userId; // 不需要解码可删除该行 session.user.roles = token.roles; // 不需要解码可删除该行 return session; } } })
前端使用方式
后续调用Rails接口时,直接从useSession中取rawToken放到请求头即可:
import { useSession } from "next-auth/react"; const { data: session } = useSession(); // 发起请求时添加授权头 const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/xxx`, { headers: { Authorization: `Bearer ${session.rawToken}` } })
内容的提问来源于stack exchange,提问作者Jan
相关产品推荐
相关产品推荐

