Attempting to make firebase.auth().currentUser a promise

ぃ、小莉子 提交于 2021-02-02 09:52:10

问题


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

回答1:


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.




回答2:


You can use the signInWithRedirect method with AuthProvider of your choice along with the getRedirectResult method . For example with 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());
    }
});

More info: https://firebase.google.com/docs/reference/js/firebase.auth.Auth#methods



来源:https://stackoverflow.com/questions/63047603/attempting-to-make-firebase-auth-currentuser-a-promise

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!