为什么 'user' 在 onAuthStateChanged (Firebase 9 + Vue 3 + Pinia) 中总是 'null'?

Why 'user' is always 'null' in onAuthStateChanged (Firebase 9 + Vue 3 + Pinia)?

我对 Firebase 的 onAuthStateChanged 有问题,无论我尝试了什么,在页面重新加载后总是 returns user 作为 null。要求的行为是在页面重新加载后自动登录用户。

那是我第一次使用模块化版本的 Firebase 9 和 Pinia 作为商店。

我已经尝试使用 setPersistence(本地),但结果是一样的。

我卡了一整天,不知道为什么它不起作用。

如果能指出我遗漏的内容,我将不胜感激。

我在 package.json 中使用的软件包:

"dependencies": {
    "firebase": "^9.6.11",
    "pinia": "^2.0.13",
    "vue": "^3.2.13",
    "vue-router": "^4.0.3"
  }

main.ts 文件:

import { createApp, App as VueApp } from 'vue';
import { createPinia } from 'pinia';
import { auth } from '@/firebase';
import { onAuthStateChanged, User } from 'firebase/auth';
import { useAuthStore } from '@/store/auth';

let app: VueApp;

function initializeApp(user: User | null) {
  app = createApp(App)
    .use(createPinia())
    .use(router);

  if (user) {
    // PROMEM IS THERE -> user IS ALWAYS null
    // I want to set logged user before app is mounted
    const authStore = useAuthStore();
    authStore.handleAuthStateChange(user);
  }

  app.mount('#app');
}

onAuthStateChanged(
  auth,
  (user: User | null) => {
    // PROMEM IS THERE -> user IS ALWAYS null
    if (!app) {
      initializeApp(user);
    } else {
      const authStore = useAuthStore();
      authStore.handleAuthStateChange(user);
    }
  },
  (error: Error) => {
    log.error(`Main AuthStateChange handler failed with error`, error);
  },
);

firebase.ts 文件:

import { FirebaseApp, initializeApp } from 'firebase/app';
import { Auth, initializeAuth, debugErrorMap } from 'firebase/auth';
import { firebaseConfig } from '@/config';

export const app: FirebaseApp = initializeApp(firebaseConfig);
export const auth: Auth = initializeAuth(app, { errorMap: debugErrorMap });

auth.ts -> 存储文件:

import { defineStore } from 'pinia';
import { LoginCredentials, SignUpCredentials } from '@/types/auth';
import { FirebaseAuthenticationService, AuthenticationService } from '@/service/AuthenticationService';
import { auth as firebaseAuth } from '@/firebase';
import { log } from '@/service/LoggerService';

export interface AuthStoreUser {
  uid: string,
  email: string | null
}

export type MaybeAuthStoreUser = AuthStoreUser | null;

export interface AuthStoreState {
  user: AuthStoreUser | null,
}

export const authStoreFactory = ($auth: AuthenticationService) => defineStore('auth', {
  state: () => ({
    user: null,
  } as AuthStoreState),
  getters: {
    isUserLoggedIn(): boolean {
      return !!this.user;
    },
  },
  actions: {
    async signUpUser(credentials: SignUpCredentials) {
      const createdUser = await $auth.createUserWithEmailAndPassword(credentials);
    },
    async loginUser(credentials: LoginCredentials) {
      const user = await $auth.signInWithEmailAndPassword(credentials);
    },
    async setCurrentUser(user: AuthStoreUser) {
      this.user = user;
    },
    async clearCurrentUser() {
      this.user = null;
    },
    async logoutUser() {
      await $auth.signOut();
    },
    async sendPasswordResetEmail(email: string) {
      await $auth.sendPasswordResetEmail(email);
    },
    async handleAuthStateChange(user: MaybeAuthStoreUser) {
      if (user) {
        log.debug(`Logging in user from authStateChange handler`);
        this.setCurrentUser(user);
      } else {
        log.debug(`AuthStateChange handler did not receive current user.`);
        this.clearCurrentUser();
      }
    },
  },
});

export const useAuthStore = () => {
  const $auth = new FirebaseAuthenticationService(firebaseAuth);
  return authStoreFactory($auth)();
};

我终于通过在 initializeAuth 中添加 persistence 选项解决了这个问题。这部分的 documentation 具有误导性,并且没有正确解释应该如何使用 initializeAuth 方法完成...

这是 authfirebase.ts 文件中的初始化方式:

import { firebaseConfig } from '@/config';
import { FirebaseApp, initializeApp } from 'firebase/app';
import { 
  Auth, 
  initializeAuth, 
  debugErrorMap,
  indexedDBLocalPersistence, 
  browserLocalPersistence } from 'firebase/auth';

export const app: FirebaseApp = initializeApp(firebaseConfig);
export const auth: Auth = initializeAuth(app, { 
  persistence: [indexedDBLocalPersistence, browserLocalPersistence],
  errorMap: debugErrorMap 
});

我在 docs 中发现的内容很有趣:

The default for web browser and React Native apps is local (provided the browser supports this storage mechanism, eg. 3rd party cookies/data are enabled) whereas it is none for Node.js backend apps.

这是我在 auth-public.d.ts 文件中找到的:

export declare interface Dependencies {
    /**
     * Which {@link Persistence} to use. If this is an array, the first
     * `Persistence` that the device supports is used. The SDK searches for an
     * existing account in order and, if one is found in a secondary
     * `Persistence`, the account is moved to the primary `Persistence`.
     *
     * If no persistence is provided, the SDK falls back on
     * {@link inMemoryPersistence}.
     */
    persistence?: Persistence | Persistence[];
    // other stuff
}

If no persistence is provided, the SDK falls back on inMemoryPersistence

inMemoryPersistence意味着NONE坚持。这就是我的问题的原因。