You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 07:09:26