试图让 firebase.auth().currentUser 成为一个承诺
Posted
技术标签:
【中文标题】试图让 firebase.auth().currentUser 成为一个承诺【英文标题】:Attempting to make firebase.auth().currentUser a promise 【发布时间】: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 我已经看到了线程,但解决方案似乎没有处理超时,如果用户对象没有被填充,就会卡住。 【参考方案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
的函数。
在我的路由器中,我使用名为 public
的 meta
标志来确定路由是否可公开访问(大多数路由没有它,这意味着它们需要身份验证)。
我的全局导航守卫看起来像这样
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 Promise
。 onAuthStateChangedPromise
是一个 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
【讨论】:
以上是关于试图让 firebase.auth().currentUser 成为一个承诺的主要内容,如果未能解决你的问题,请参考以下文章
使用 firebase auth 和 firestore 在流中调用未来
如何让 Firebase auth OAuthProvider 使用 OIDC 提供程序添加其他范围