Vue3中Pinia存储Firebase accessToken丢失问题求助
问题:Pinia中存储Firebase AuthResult后,Axios拦截器无法获取accessToken
我是Vue和Pinia的新手,正在搭建Firebase、Pinia与Axios的集成。应用使用FirebaseUI通过邮箱链接完成用户登录,登录成功后将signInSuccessWithAuthResult返回的AuthResult对象存入Pinia的user状态,调试时能看到accessToken存在,但在Axios拦截器中尝试获取时,accessToken却消失了,用户对象的其他属性大多还在。
相关代码
1. LoginPage组件代码
<script setup lang="ts"> import { onMounted } from "vue"; import { EmailAuthProvider } from "firebase/auth"; import { auth } from "firebaseui"; import { auth as firebaseAuth } from "../firebase/config"; import { useUserStore } from "../stores/user" onMounted(async () => { const uiConfig: auth.Config = { signInSuccessUrl: "/", signInOptions: [ { provider: EmailAuthProvider.PROVIDER_ID, signInMethod: EmailAuthProvider.EMAIL_LINK_SIGN_IN_METHOD, forceSameDevice: true, }, ], callbacks: { signInSuccessWithAuthResult: function (authResult) { const store = useUserStore(); store.user = authResult; return true; }, }, }; const ui = new auth.AuthUI(firebaseAuth); ui.start("#firebaseui-auth-container", uiConfig); }); </script> <template> <div id="firebaseui-auth-container"></div> </template>
2. 用户Store代码
import { defineStore } from 'pinia' export const useUserStore = defineStore("userStore", { state: () => ({ user: null as any }), getters: { getUser(state) { return state.user } } })
3. Axios拦截器代码
axiosInstance.interceptors.request.use((config) => { const userStore = useUserStore(); if (userStore) { // accessToken is undefined config.headers.Authorization = 'Bearer ' + userStore.user.user.accessToken; } return config; });
问题原因
Firebase返回的AuthResult中的user是一个Firebase User类实例,而非普通JSON对象。这个实例包含很多不可枚举的属性、getter方法以及内部状态,当直接将其存入Pinia时,Pinia的状态序列化机制会将类实例转换为普通对象,导致那些不可枚举的属性(比如accessToken)丢失。
解决方案
方案1:提取需要的属性存储(推荐)
不要直接存储AuthResult或User实例,而是提取你需要的具体属性(比如accessToken、uid、email等),存为普通对象:
修改LoginPage中的登录回调:
signInSuccessWithAuthResult: function (authResult) { const store = useUserStore(); // 提取需要的用户属性 const userData = { uid: authResult.user.uid, email: authResult.user.email, accessToken: authResult.user.accessToken, displayName: authResult.user.displayName // 按需添加其他需要的属性 }; store.user = userData; return true; }
方案2:使用Firebase官方监听实时同步用户状态
更可靠的方式是使用Firebase的onAuthStateChanged监听用户登录状态变化,自动同步到Pinia,避免依赖登录回调:
在项目入口文件(比如main.ts)中添加:
import { onAuthStateChanged } from "firebase/auth"; import { auth } from "./firebase/config"; import { useUserStore } from "./stores/user"; // 监听用户认证状态变化 onAuthStateChanged(auth, (user) => { const userStore = useUserStore(); if (user) { // 用户已登录,提取属性存入Pinia userStore.user = { uid: user.uid, email: user.email, accessToken: user.accessToken, displayName: user.displayName }; } else { // 用户已登出,清空状态 userStore.user = null; } });
方案3:直接从Firebase获取最新token(最可靠)
如果担心Pinia中存储的token过期,或者不想处理实例序列化问题,可以直接在拦截器中从Firebase Auth实例获取当前用户的有效token:
import { auth } from "../firebase/config"; axiosInstance.interceptors.request.use(async (config) => { const currentUser = auth.currentUser; if (currentUser) { // 实时获取最新的有效token(Firebase会自动刷新过期token) const accessToken = await currentUser.getIdToken(); config.headers.Authorization = `Bearer ${accessToken}`; } return config; });
最终调整后的Axios拦截器示例
结合方案1或方案2,修改拦截器:
axiosInstance.interceptors.request.use((config) => { const userStore = useUserStore(); if (userStore.user) { config.headers.Authorization = `Bearer ${userStore.user.accessToken}`; } return config; });
内容的提问来源于stack exchange,提问作者Joe Morgan
相关产品推荐
相关产品推荐

