Почему «пользователь» всегда «нулевой» в onAuthStateChanged (Firebase 9 + Vue 3 + Pinia)?

У меня проблема с Firebase onAuthStateChanged, который, что бы я ни пробовал, всегда возвращает 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)();
};

Вы можете ожидать, что пользователь всегда будет нулевым после первой загрузки или обновления страницы. Пользовательский объект прибудет через некоторое время во втором обратном вызове, если пользователь вошел в систему.

Doug Stevenson 23.04.2022 15:03

@DougStevenson, но «через некоторое время во втором обратном вызове» никогда не бывает. У меня есть форма входа -> пользователь входит в систему -> пользователь перенаправляется на страницу панели инструментов -> все в порядке до перезагрузки страницы. После этого пользователь становится нулевым, и обратный вызов не вызывается через некоторое время. Любые другие идеи или советы? :/

Łukasz Ciesielski 23.04.2022 15:17

@DougStevenson: onAuthStateChanged сработает только в первый раз после завершения восстановления учетных данных из локального хранилища. Так что, если это срабатывает с null, восстановление не удалось (или восстанавливать было нечего).

Frank van Puffelen 23.04.2022 15:54

Я сделал тест и вернулся к версии firebase с пространством имен вместо модульной. Код работает должным образом, и пользователь правильно вошел в систему после перезагрузки страницы. Итак, я что-то пропустил или неправильно понял документацию firebase относительно модульной библиотеки. Если кто-то сможет найти ошибку в коде, который я предоставил в вопросе, я был бы признателен. На данный момент версия с пространством имен - это то, что мне нужно.

Łukasz Ciesielski 23.04.2022 16:23
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Сравнение структур данных: Массивы и объекты в Javascript
Сравнение структур данных: Массивы и объекты в Javascript
Итак, вы изучили основы JavaScript и хотите перейти к изучению структур данных. Мотивация для изучения/понимания Структур данных может быть разной,...
Создание собственной системы электронной коммерции на базе Keystone.js - настройка среды и базовые модели
Создание собственной системы электронной коммерции на базе Keystone.js - настройка среды и базовые модели
Прошлая статья была первой из цикла статей о создании системы электронной коммерции с использованием Keystone.js, и она была посвящена главным образом...
Приложение для отслеживания бюджета на React js для начинающих
Приложение для отслеживания бюджета на React js для начинающих
Обучение на практике - это проверенная тема для достижения успеха в любой области. Если вы знаете контекст фразы "Практика делает человека...
Стоит ли использовать React в 2022 году?
Стоит ли использовать React в 2022 году?
В 2022 году мы все слышим о трендах фронтенда (React, Vue), но мы не знаем, почему мы должны использовать эти фреймворки, когда их использовать, а...
0
4
67
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Наконец-то я решил эту проблему, добавив опцию persistence к initializeAuth. И документация для этой части вводит в заблуждение и не объясняет должным образом, как это должно быть сделано методом initializeAuth...

Вот как auth должен быть инициализирован в firebase.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 
});

Интересно, что я нашел в документы:

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 настойчивость. И это было причиной моей проблемы.

Другие вопросы по теме