试图使firebase.auth().currentUser成为一个承诺 [英] Attempting to make firebase.auth().currentUser a promise

查看:37
本文介绍了试图使firebase.auth().currentUser成为一个承诺的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

这是我要尝试执行的操作,以使路由等待一段时间以查看身份验证是否更改.我想知道代码是否存在任何问题,或者是否有更好的编写方法.

P.S.我知道我可以检查 firebase.auth().onAuthStateChanged ,但是我不明白如何在用户创建或登录后使用它.它只在后台运行,我可以只需在无上下文身份验证更改时向用户发送路由.它也没有超时.

  getUser({commit}){返回新的Promise((resolve,reject)=> {让用户,i = 0函数checkForUser(){setTimeout(()=> {我++用户= firebase.auth().currentUser如果(使用者){router.push({path:'/restricted'})解决()}如果(i> 30){reject({消息:出了点问题,请重试."})} 别的 {checkForUser()}},100)}checkForUser()})}, 

解决方案

这是一个很长的答案,因为Firebase尚未出现在桌前并且对 状态:{用户:null},吸气剂:{isAuthenticated:状态=>typeof state.user ==='object'&&state.user!==空},突变:{登录:(状态,用户)=>(state.user =用户),注销:状态=>(state.user = null)}

我有一个用于配置Firebase App的 firebase/index.js 文件

 从'firebase/app'导入firebase导入"firebase/auth"//导入任何其他Firebase库,例如firestore等从'./config'导入配置从'../store'导入商店//导入Vuex商店firebase.initializeApp(config)导出const auth = firebase.auth()//导出其他Firebase位,例如db等const onAuthStateChangedPromise =新的Promise((解决,拒绝)=> {auth.onAuthStateChanged(user => {store.commit(用户!== null?'登录':'退出',用户)解决(用户)},err =>{拒绝(错误)})})导出const onAuthStateInit =()=>onAuthStateChangedPromise 

onAuthStateChanged 侦听器使商店与用户的 auth 状态保持同步,但外部承诺仅解析一次(随后对 resolve()的调用>会被忽略).由于承诺的这一功能, onAuthStateChangedPromise 可用于检测Firebase身份验证系统何时完成其初始化阶段.

然后我将该承诺作为名为 onAuthStateInit 的函数公开.

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

我的全球导航卫兵看起来像这样

 从'./firebase'导入{onAuthStateInit}//定义路线,新的VueRouter等router.beforeEach(async(to,from,next)=> {await onAuthStateInit()//等待身份验证系统初始化if(to.matched.every(route => route.meta.public)|| store.getters.isAuthenticated){下一个()} 别的 {next({name:'sign-in'})//重定向到登录页面}}) 

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

我的登录页面使用Firebase Auth UI,并定义了以下回调

 从'firebase/app'导入firebase从'firebaseui'导入*作为firebaseui从"../firebase"导入{auth}导入'firebaseui/dist/firebaseui.css'const ui = new firebaseui.auth.AuthUI(auth)导出默认值{已安装(){//引用只是一个< div>在我的模板中const容器= this.$ refs.firebaseuiAuthContainerui.start(容器,{//signInOptions等回调:{signInSuccessWithAuthResult:authResult =>{this.$ router.push({name:'home'})//转到主页或任何地方},signInFailure:错误=>{//处理登录错误}}})}} 

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

When a user logs in, I'm trying to send them to a restricted page. So I'm protecting the route by checking for the user object. The problem is that when a user is created or logging in, the auth doesn't immediately change, so after creating or logging in, firebase.auth().currentUser can return null for a couple of milliseconds. So if I send them to the page, it will return an error.

This is what I'm trying to do in an attempt for the route to wait a while to see if the auth changes. I'm wondering if there is any issues with the code or a better way to write this.

P.S. I know that I can check for firebase.auth().onAuthStateChanged, but I don't understand how I can use it upon user creation or log in. It just runs in the background and I can't just send users a route when auth changes with no context. It also doesn't have a timeout.

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()
  } )
},

解决方案

This is a long answer because Firebase has yet to come to the table and do anything about https://github.com/firebase/firebase-js-sdk/issues/462


Here's how I've done it in the past, synchronising the user in Vuex.

For this, I've got two mutations and a handy getter but there's nothing too special there

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)
}

I have a firebase/index.js file for configuring the Firebase App that looks like this

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

The onAuthStateChanged listener keeps the store in sync with the user's auth status yet the outer promise only resolves once (subsequent calls to resolve() are ignored). Due to this feature of promises, the onAuthStateChangedPromise can be used to detect when the Firebase authentication system has completed its initialisation phase.

I've then exposed that promise as a function named onAuthStateInit.

In my router, I use a meta flag named public to determine if a route is publicly accessible or not (most routes don't have it which means they need authentication).

My global navigation guard looks like this

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
  }
})

You can use await onAuthStateInit() anywhere where you would need to wait for that first-page-load auth initialisation to complete. You can call this as many times as is necessary and it will only ever wait one time. Once the auth initialisation is complete, this promise will resolve instantly.

My sign-in page uses the Firebase Auth UI with the following callbacks defined

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
        }
      }
    })    
  }
}

You don't have to use the Auth UI. Any changes to the authenticated state of the current user will be caught by the onAuthStateChange listener so you can use the manual auth.SignInWith*() methods if you want.

这篇关于试图使firebase.auth().currentUser成为一个承诺的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆