Firebase认证延迟导致SvelteKit页面因Firestore规则刷新报错
问题原因分析
你的猜测完全正确——问题出在Firebase认证状态的初始化时机上。
SvelteKit的__layout.svelte中,load函数返回的authUser是一个未解析的Promise,组件直接用{#if authUser}判断时,Promise会被视为“真值”,导致页面直接渲染<slot>(即需要读取Firestore数据的页面内容)。此时Firebase Auth还未完成状态确认,发起的Firestore请求会因为request.auth为null触发权限规则,进而抛出Missing or insufficient permissions错误,导致页面500空白。
而导航后返回时,客户端已经缓存了认证状态,Firestore请求能携带有效认证信息,所以页面正常渲染。
解决方案
1. 修正Layout的Load函数,等待认证状态就绪
修改__layout.svelte中的load函数,等待认证状态Promise解析完成后再返回结果,确保组件拿到的是最终的用户状态,而非Promise:
<script lang="ts" context="module"> import type { User } from 'firebase/auth'; import { onAuthStateChanged } from "firebase/auth"; import { auth } from "$lib/scripts/firebaseStart"; export async function load() { async function getAuthUser() { return new Promise<User | false>((resolve) => { onAuthStateChanged(auth, (user) => { resolve(user ? user : false); }); }); } // 等待认证状态解析完成 const authUser = await getAuthUser(); return { props: { authUser } }; } </script> <script lang="ts"> import "../styles/global.css"; import type { User } from 'firebase/auth'; export let authUser: User | false; </script> <main> <!-- 处理三种状态:未就绪、已登录、未登录 --> {#if authUser === undefined} <div>加载中...</div> {:else if authUser} <slot /> {:else} <!-- 未登录时跳转到登录页 --> <script> import { goto } from '$app/navigation'; goto('/login'); </script> {/if} </main>
2. 页面级数据加载需依赖认证状态
如果页面自身的load函数需要请求Firestore数据,必须确保先拿到有效的authUser,避免未认证请求:
// 示例页面的load函数 export async function load({ props }) { const { authUser } = props; // 未登录直接重定向 if (!authUser) { return { redirect: '/login' }; } // 此时auth状态已就绪,安全发起Firestore请求 const db = getFirestore(); const docRef = doc(db, 'collection', 'doc-id'); const docSnap = await getDoc(docRef); return { props: { data: docSnap.data() } }; }
3. 可选:添加客户端认证状态缓存
为了优化用户体验,可以在客户端维护认证状态的全局存储(比如用Svelte的writable store),避免每次页面切换都重新监听onAuthStateChanged:
// $lib/stores/auth.ts import { writable } from 'svelte/store'; import type { User } from 'firebase/auth'; import { auth } from '../scripts/firebaseStart'; import { onAuthStateChanged } from 'firebase/auth'; export const authUser = writable<User | null>(null); onAuthStateChanged(auth, (user) => { authUser.set(user); });
然后在Layout和页面中直接使用这个store,减少重复逻辑。
内容的提问来源于stack exchange,提问作者bidotdableudot
相关产品推荐
相关产品推荐

