SvelteKit +page.ts load中FirebaseAuth用户始终为null的解决方法咨询
解决方案
一、客户端Load函数修复(保持SSR=false)
问题核心是Firebase Auth的会话恢复是异步过程,load函数执行时Auth还未完成初始化,导致currentUser为null。可以通过等待Auth状态变化确保拿到用户:
- 封装等待用户加载的工具函数:
import { type Auth, onAuthStateChanged } from "firebase/auth"; function waitForUser(auth: Auth): Promise<User | null> { return new Promise((resolve) => { const unsubscribe = onAuthStateChanged(auth, (user) => { unsubscribe(); resolve(user); }); }); }
- 修改
+page.ts的load函数:
import { getAuth } from "firebase/auth"; import { error } from "@sveltejs/kit"; import type { PageLoad } from "./$types"; export const load = (async () => { const auth = getAuth(); // 等待Auth完成会话恢复,拿到用户 const user = await waitForUser(auth); if (!user) throw error(401, "Could not authenticate"); const token = await user.getIDToken(); const data = await api.requestData(token); return { data }; }) satisfies PageLoad; export const ssr = false;
这个方案会让load函数等待用户状态加载完成后再请求数据,避免页面渲染后二次请求。
二、服务端认证方案(更推荐)
SvelteKit完全支持服务端认证,这种方式更安全,且无需依赖客户端Firebase初始化:
步骤1:客户端登录后存储ID Token到Cookie
登录成功后,将ID Token存入HttpOnly Cookie(防止XSS攻击):
import { getAuth, signInWithPopup, GoogleAuthProvider } from "firebase/auth"; async function handleGoogleLogin() { const auth = getAuth(); const provider = new GoogleAuthProvider(); const result = await signInWithPopup(auth, provider); const token = await result.user.getIDToken(); // 设置Cookie,有效期与ID Token一致(默认1小时) document.cookie = `id_token=${token}; HttpOnly; Secure; SameSite=Strict; Max-Age=3600; Path=/`; // 登录成功后跳转或刷新页面 window.location.href = "/"; }
步骤2:服务端验证Token并注入用户信息
在hooks.server.ts中验证Cookie里的ID Token,将用户信息存入locals:
import { adminAuth } from "$lib/server/firebase-admin"; // 已初始化的Firebase Admin实例 import type { Handle } from "@sveltejs/kit"; export const handle: Handle = async ({ event, resolve }) => { const token = event.cookies.get("id_token"); if (token) { try { // 验证ID Token合法性 const decodedToken = await adminAuth.verifyIdToken(token); event.locals.user = { uid: decodedToken.uid, email: decodedToken.email }; } catch (err) { // 验证失败,清除无效Cookie event.cookies.delete("id_token"); } } return resolve(event); };
步骤3:服务端Load函数获取数据
改用+page.server.ts的服务端Load函数,直接通过locals获取用户信息并请求数据:
import { error } from "@sveltejs/kit"; import type { PageServerLoad } from "./$types"; import { api } from "$lib/server/api"; // 服务端API请求工具 export const load: PageServerLoad = async ({ locals }) => { if (!locals.user) throw error(401, "Could not authenticate"); // 直接使用验证后的用户信息请求数据(无需再传Token到API,或根据API要求处理) const data = await api.requestData(locals.user.uid); return { data }; };
注意事项
- Firebase Admin需提前初始化:使用服务账号密钥创建Admin实例,确保服务端能验证Token。
- Token刷新:ID Token有效期为1小时,需在客户端监听
onIdTokenChanged事件,自动更新Cookie。 - Cookie安全性:生产环境务必开启
Secure属性(仅HTTPS传输),避免Cookie被窃取。
内容的提问来源于stack exchange,提问作者Sisson
相关产品推荐
相关产品推荐

