NextAuth V5如何获取JWT Token并用于Express鉴权
获取NextAuth自动生成的JWT Token用于后端鉴权
我知道NextAuth会自动生成JWT Token,但不清楚如何获取该Token,以便添加到请求头中调用后端需要鉴权的接口。以下是我的相关代码和问题:
1. NextAuth配置(auth.ts)
import NextAuth from "next-auth"; import Google from "next-auth/providers/google"; import Credentials from "next-auth/providers/credentials"; import axios from "axios"; export const { handlers, signIn, signOut, auth } = NextAuth({ session: { strategy: "jwt", maxAge: 30 * 24 * 60 * 60, }, providers: [ Google({ clientId: process.env.AUTH_GOOGLE_ID, clientSecret: process.env.AUTH_GOOGLE_SECRET, }), Credentials({ name: "Credentials", credentials: { email: { label: "Email", type: "email" }, password: { label: "Password", type: "password" }, }, authorize: async (credentials) => { try { const res = await axios.post('http://localhost:8080/api/auth/login', { email: credentials.email, password: credentials.password, }); const user = res.data; if (user) { return user; } else { return null; } } catch (error) { console.error("Login error:", error); return null; } }, }), ], callbacks: { async signIn({ user, account }) { if (account?.provider === 'google') { const res = await fetch('http://localhost:8080/api/auth/google-signin', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ idToken: account.id_token, }), }); if (!res.ok) { console.error("Failed to create user"); return false; } const userData = await res.json(); if (userData && userData.role) { user.role = userData.role; } } return true; }, async jwt({ token, user }) { return {...token, ...user}; }, async session({ session, token }) { session.user = token as any; return session; }, }, secret: process.env.AUTH_SECRET, pages: { signIn: "/login", }, });
2. 当前获取的Session对象
{ "user": { "name": "testuser", "email": "testuser@test.com", "picture": "https://lh3.googleusercontent.com/a/ACg8ocJ-Cr0XVdUf5HfHhOvsVZuC3ShYaNPWv-cAjJTRs5mhoF91cw=s96-c", "sub": "66a8c9b102c0bd61a0a65639", "id": "66a8c9b102c0bd61a0a65639", "role": "user", "image": "https://lh3.googleusercontent.com/a/ACg8ocJ-Cr0XVdUf5HfHhOvsVZuC3ShYaNPWv-cAjJTRs5mhoF91cw=s96-c", "iat": 1722577679, "exp": 1725169679, "jti": "17493ea5-a58d-4a1c-ac3a-102039df7856" }, "expires": "2024-09-01T05:48:00.555Z" }
可以看到Session中没有直接提供Token字符串,无法直接添加到请求头。
3. 后端Express鉴权中间件
import * as jwt from "jsonwebtoken"; import { Request, Response, NextFunction } from "express"; const jwtSecretKey = process.env.AUTH_SECRET; interface IIsAuth extends Request { userId?: string; userRole?: string; } const checkAuth = (req: IIsAuth, res: Response, next: NextFunction) => { try { const token = (req.headers.authorization || "").replace(/Bearer\s?/, ""); const decodedToken = jwt.verify(token, jwtSecretKey) as { UserInfo: { _id: string, role: string } }; req.userId = decodedToken.UserInfo._id; req.userRole = decodedToken.UserInfo.role; next(); } catch (error) { console.error("Error authenticating user:", error); res.status(403).json({ message: "Unauthorized" }); } }; export default checkAuth;
我希望能在Session中获取到Token,或者了解如何正确将Token添加到请求头中。我认为无需在后端单独生成JWT,因为NextAuth应该会自动处理。
解决方案
方案一:使用NextAuth生成的JWT对接后端鉴权
NextAuth生成的JWT默认不会暴露在Session中,但可以通过getToken方法获取原始Token字符串,同时需要调整JWT结构匹配后端鉴权逻辑:
- 调整NextAuth的JWT回调,匹配后端结构
修改callbacks.jwt,构造后端期望的UserInfo字段:
async jwt({ token, user }) { if (user) { // 添加后端鉴权需要的UserInfo结构 token.UserInfo = { _id: user.id, role: user.role }; } return {...token, ...user}; }
- 获取JWT字符串
- 服务器组件/API路由中直接获取:
import { auth, getToken } from '@/auth'; export default async function ServerComponent() { const session = await auth(); // 获取NextAuth生成的原始JWT字符串 const jwtToken = await getToken({ req }); // 调用后端接口时携带Token const apiRes = await fetch('http://localhost:8080/api/protected', { headers: { Authorization: `Bearer ${jwtToken}` } }); }
- 客户端组件中通过API路由中转:
先创建一个API路由app/api/get-jwt/route.ts:
import { getToken } from 'next-auth/jwt'; import { NextResponse } from 'next/server'; export async function GET(req: Request) { const token = await getToken({ req, secret: process.env.AUTH_SECRET }); return NextResponse.json({ token }); }
然后在客户端组件中调用该接口获取Token:
'use client'; import { useEffect, useState } from 'react'; export default function ClientComponent() { const [jwtToken, setJwtToken] = useState<string | null>(null); useEffect(() => { const fetchToken = async () => { const res = await fetch('/api/get-jwt'); const data = await res.json(); setJwtToken(data.token); }; fetchToken(); }, []); // 调用后端接口 const callProtectedApi = async () => { if (!jwtToken) return; const res = await fetch('http://localhost:8080/api/protected', { headers: { Authorization: `Bearer ${jwtToken}` } }); }; return <button onClick={callProtectedApi}>调用保护接口</button>; }
方案二:使用后端返回的Token(针对Credentials登录)
如果你的后端登录接口会返回自己的JWT,可以将其存入NextAuth的Session中:
- 在Credentials授权中保存后端Token
修改authorize函数,将后端返回的Token添加到user对象:
authorize: async (credentials) => { try { const res = await axios.post('http://localhost:8080/api/auth/login', { email: credentials.email, password: credentials.password, }); const user = res.data; // 假设后端返回的Token字段为token user.backendToken = res.data.token; if (user) { return user; } else { return null; } } catch (error) { console.error("Login error:", error); return null; } },
- 将Token传递到Session
通过jwt和session回调,将Token添加到Session中:
async jwt({ token, user }) { return {...token, ...user}; }, async session({ session, token }) { session.user = token as any; // 将后端Token添加到Session的user对象 session.user.backendToken = token.backendToken; return session; },
- 前端获取Token并使用
从Session中取出backendToken,添加到请求头:
'use client'; import { useSession } from 'next-auth/react'; export default function ClientComponent() { const { data: session } = useSession(); const callProtectedApi = async () => { if (!session?.user?.backendToken) return; const res = await fetch('http://localhost:8080/api/protected', { headers: { Authorization: `Bearer ${session.user.backendToken}` } }); }; return <button onClick={callProtectedApi}>调用保护接口</button>; }
内容的提问来源于stack exchange,提问作者Tall Lorenc
相关产品推荐
相关产品推荐

