【问题标题】:Attempting to make firebase.auth().currentUser a promise试图让 firebase.auth().currentUser 成为一个承诺
【发布时间】:2020-11-12 18:53:03
【问题描述】:

当用户登录时,我试图将他们发送到受限页面。所以我通过检查用户对象来保护路由。问题是当用户创建或登录时,auth 不会立即更改,因此在创建或登录后,firebase.auth().currentUser 可以在几毫秒内返回null。因此,如果我将它们发送到页面,它将返回错误。

这就是我试图让路由等待一段时间以查看身份验证是否更改的尝试。我想知道代码是否有任何问题或编写此代码的更好方法。

附:我知道我可以检查firebase.auth().onAuthStateChanged,但我不明白如何在用户创建或登录时使用它。它只是在后台运行,当身份验证更改时我不能只向用户发送路由没有语境。它也没有超时。

getUser( { commit } ) {
  return new Promise( ( resolve, reject )=> {
    let user, i = 0
    function checkForUser() {
      setTimeout( ()=> {
        i++
        user = firebase.auth().currentUser
        if ( user ) {
          router.push( { path: '/restricted' } )
          resolve()
        } else if ( i > 30 ) {
          reject( { message: 'Something went wrong, please try again.' } )
        } else {
          checkForUser()
        }
      }, 100 )
   }  
   checkForUser()
  } )
},

【问题讨论】:

  • 似乎您还想显示登录用户的代码,因为您在此处显示的内容取决于其他代码,以及您可能如何处理其结果。跨度>
  • 有一个很长的 GitHub 问题(他们需要很长时间才能修复 IMO),列出了一些很好的解决方法 ~ github.com/firebase/firebase-js-sdk/issues/462
  • @DougStevenson 我实际上在需要用户对象存在的几个不同地方使用它。所以我可以运行这个函数并等待它通过或失败。但不知何故感觉有点乱,想知道是否有更清洁的方法来做到这一点。
  • @Phil 我已经看到了线程,但解决方案似乎没有处理超时,如果用户对象没有被填充,就会卡住。

标签: javascript firebase vue.js firebase-authentication vuex


【解决方案1】:

这是一个很长的答案,因为 Firebase 尚未就 https://github.com/firebase/firebase-js-sdk/issues/462 做任何事情


这是我过去的做法,在 Vuex 中同步用户。

为此,我有两个突变和一个方便的吸气剂,但那里没有什么特别的

state: {
  user: null
},
getters: {
  isAuthenticated: state => typeof state.user === 'object' && state.user !== null
},
mutations: {
  LOGIN: (state, user) => (state.user = user),
  LOGOUT: state => (state.user = null)
}

我有一个 firebase/index.js 文件,用于配置如下所示的 Firebase 应用程序

import firebase from 'firebase/app'
import 'firebase/auth'
// import any other Firebase libs like firestore, etc
import config from './config'
import store from '../store' // import the Vuex store

firebase.initializeApp(config)

export const auth = firebase.auth()
// export other Firebase bits like db, etc

const onAuthStateChangedPromise = new Promise((resolve, reject) => {
  auth.onAuthStateChanged(user => {
    store.commit(user !== null ? 'LOGIN' : 'LOGOUT', user)
    resolve(user)
  }, err => {
    reject(err)
  })
})

export const onAuthStateInit = () => onAuthStateChangedPromise

onAuthStateChanged 监听器使 store 与用户的 auth 状态保持同步,但外部 Promise 仅解析一次(随后对 resolve() 的调用将被忽略)。由于 Promise 的这一特性,onAuthStateChangedPromise 可用于检测 Firebase 身份验证系统何时完成其初始化阶段。

然后我将该承诺公开为一个名为 onAuthStateInit 的函数。

在我的路由器中,我使用名为 publicmeta 标志来确定路由是否可公开访问(大多数路由没有它,这意味着它们需要身份验证)。

我的全局导航守卫看起来像这样

import { onAuthStateInit } from './firebase'

// define routes, new VueRouter, etc

router.beforeEach(async (to, from, next) => {
  await onAuthStateInit() // wait for auth system to initialise
  if (to.matched.every(route => route.meta.public) || store.getters.isAuthenticated) {
    next()
  } else {
    next({ name: 'sign-in' }) // redirect to sign-in page
  }
})

您可以在需要等待首页加载身份验证初始化完成的任何地方使用await onAuthStateInit()。您可以根据需要多次调用它,它只会等待一次。身份验证初始化完成后,此承诺将立即解决。

我的 sign-in 页面使用 Firebase 身份验证 UI,并定义了以下 回调

import firebase from 'firebase/app'
import * as firebaseui from 'firebaseui'
import { auth } from '../firebase'
import 'firebaseui/dist/firebaseui.css'

const ui = new firebaseui.auth.AuthUI(auth)

export default {
  mounted () {
    // the ref is just a <div> in my template
    const container = this.$refs.firebaseuiAuthContainer
    ui.start(container, { 
      // signInOptions, etc
      callbacks: {
        signInSuccessWithAuthResult: authResult => {
          this.$router.push({ name: 'home' }) // go to homepage or wherever
        },
        signInFailure: err => {
          // handle sign-in error
        }
      }
    })    
  }
}

您不必使用身份验证 UI。 onAuthStateChange 侦听器将捕获对当前用户身份验证状态的任何更改,因此您可以根据需要使用手动 auth.SignInWith*() 方法。

【讨论】:

  • 为什么 onAuthStateInit 只运行一次?它不是每次都创建一个新的承诺吗?
  • @KelvinZhao 不,你可以看到它返回了只创建一次的onAuthStateChangedPromise。我只是把它做成一个函数,因为await onAuthStateInit() 看起来比await onAuthStateChangedPromise 更好
  • 啊...所以当你return new Promise,它会在第一次解析时锁定它,而如果你new Promise,它每次都会运行一个新的承诺?是这样的吗?
  • @KelvinZhao 我的回答中没有return new PromiseonAuthStateChangedPromise 是一个 Promise,它只被创建一次。 onAuthStateInit 函数每次调用时都会返回相同的承诺
  • 对不起,最后一个问题,当用户退出,然后重新登录时会发生什么。onAuthStateInit 将返回为真,但仍有一瞬间用户正在登录,因此,如果我在那一瞬间之前将它们指向受限页面,它将弹回主页。
【解决方案2】:

您可以将signInWithRedirect 方法与您选择的AuthProvider 以及getRedirectResult 方法一起使用。以 Google 为例:

firebase.auth().onAuthStateChanged(function(user) {
    if (user) {
        firebase.auth().getRedirectResult().then(function(result) {
            if (result.additionalUserInfo && result.additionalUserInfo.isNewUser) {
                // User just created.
            }
            if (result.user) {
                // User just signed in.
            }
        });
        // User is signed in.
    }
    else {
        // User is signed out.
        firebase.auth().signInWithRedirect(new firebase.auth.GoogleAuthProvider());
    }
});

更多信息:https://firebase.google.com/docs/reference/js/firebase.auth.Auth#methods

【讨论】:

    猜你喜欢
    • 2017-12-06
    • 2018-12-06
    • 1970-01-01
    • 2021-09-16
    • 1970-01-01
    • 1970-01-01
    • 2017-10-04
    • 1970-01-01
    • 2018-05-07
    相关资源
    最近更新 更多