Next Auth集成Firebase Auth:如何存储获取Bearer令牌用于API请求
实现Firebase idToken的存储与API请求携带
步骤1:修改Next Auth的authorize函数,获取并返回Firebase idToken
在authorize方法中调用Firebase的getIdToken()获取令牌,将其与用户邮箱一起返回:
async authorize(credentials, req) { try { const userCredential = await signInWithEmailAndPassword( auth, credentials.email, credentials.password ); // 获取Firebase的idToken(异步方法) const idToken = await userCredential.user.getIdToken(); if (userCredential.user) { return { email: userCredential.user.email, idToken: idToken }; } return null; } catch (error) { throw new Error('Invalid email or password'); } }
步骤2:配置Next Auth的会话策略与回调,注入idToken到会话
Credentials Provider仅支持JWT会话策略,显式配置后通过jwt和session回调将idToken存入会话:
export default NextAuth({ secret: process.env.NEXTAUTH_SECRET, pages: { signIn: '/auth/signin', }, // 配置会话为JWT策略 session: { strategy: "jwt", }, providers: [/* 原CredentialsProvider代码 */], callbacks: { // 将idToken存入JWT令牌 async jwt({ token, user }) { // 登录时,user是authorize返回的对象 if (user) { token.idToken = user.idToken; token.email = user.email; // 记录令牌生成时间和过期时间(Firebase令牌有效期1小时) token.iat = Math.floor(Date.now() / 1000); token.exp = token.iat + 3600; } return token; }, // 将idToken从JWT提取到前端可用的session对象 async session({ session, token }) { session.user.idToken = token.idToken; session.user.email = token.email; return session; }, }, });
步骤3:前端获取idToken并在API请求中携带
使用Next Auth的useSession钩子获取会话中的idToken,发起请求时添加到Authorization头:
import { useSession } from "next-auth/react"; function DataFetchComponent() { const { data: session } = useSession(); const fetchProtectedData = async () => { if (!session?.user?.idToken) { alert("请先登录"); return; } const response = await fetch("/api/protected", { method: "GET", headers: { Authorization: `Bearer ${session.user.idToken}`, }, }); if (!response.ok) { console.error("请求失败"); return; } const data = await response.json(); console.log("获取到的数据:", data); }; return ( <button onClick={fetchProtectedData}>获取受保护数据</button> ); } export default DataFetchComponent;
步骤4:(可选)自动刷新过期的idToken
Firebase idToken有效期为1小时,在jwt回调中添加逻辑自动刷新:
async jwt({ token, user }) { if (user) { token.idToken = user.idToken; token.email = user.email; token.iat = Math.floor(Date.now() / 1000); token.exp = token.iat + 3600; } // 当前时间离过期还有5分钟以上,无需刷新 const now = Math.floor(Date.now() / 1000); if (token.exp - now > 300) { return token; } // 强制刷新令牌 try { const currentUser = auth.currentUser; if (currentUser) { const newIdToken = await currentUser.getIdToken(true); return { ...token, idToken: newIdToken, iat: Math.floor(Date.now() / 1000), exp: Math.floor(Date.now() / 1000) + 3600, }; } return token; } catch (error) { console.error("刷新idToken失败:", error); return token; } }
步骤5:后端API验证idToken(可选)
如果需要在后端验证令牌有效性,使用Firebase Admin SDK:
// /api/protected/route.js import { adminAuth } from "@/util/firebase-admin"; // 提前初始化Firebase Admin export async function GET(req) { const authHeader = req.headers.get("Authorization"); if (!authHeader || !authHeader.startsWith("Bearer ")) { return new Response(JSON.stringify({ error: "未授权" }), { status: 401 }); } const idToken = authHeader.split(" ")[1]; try { // 验证并解码idToken const decodedToken = await adminAuth.verifyIdToken(idToken); return new Response( JSON.stringify({ message: "请求成功", user: decodedToken }), { status: 200 } ); } catch (error) { return new Response(JSON.stringify({ error: "无效令牌" }), { status: 401 }); } }
内容的提问来源于stack exchange,提问作者Felipe Scherer
相关产品推荐
相关产品推荐

