0

Can anyone help me with this please with this useEffect in React? I am updating custom claims in firebase auth via a firestore document called user_claims. In here it has a user role. Instead of waiting an hour for the token to be refreshed....I want to refresh it as soon as I make a change to the user_claims collection. Based on the below I am getting the error: Unhandled Rejection (TypeError): Cannot read property 'getIdTokenResult' of undefined

Have I put my listeners in the wrong order or should I have two useEffects? Not sure what I need to do and would appreciate guidance. I want to get the user role - in real-time into the AuthContext for use throughout the app.

thanks all in advance for your help and guidance, as always.

import React, { useEffect, useState, createContext } from 'react'
import {firebase, firestore} from './firebase'

export const AuthContext = createContext()

export const AuthProvider = ({ children }) => {
  
  const auth=firebase.auth(); 

  const [currentUser, setCurrentUser] = useState();
  const [error, setError] = useState();
  

  useEffect(() => {
  
async function reportAdminStatus() {
    const result = await currentUser.getIdTokenResult(false)
    const isAdmin = result.claims.isAdmin
    if (isAdmin) {
        console.log("Custom claims say I am an admin!")
    }
    else {
        console.log("Custom claims say I am not an admin.")
    }
}
auth.onIdTokenChanged(user => {

    if (user) {
        console.log(`new ID token for ${user.uid}`)
        setCurrentUser(user)
        reportAdminStatus()
    }
})


auth.onAuthStateChanged(async (user) => {


function listenToClaims() {
  firestore
  .collection('user_claims')
  .doc(currentUser.uid)
  .onSnapshot(onNewClaims)
}

let synced
function onNewClaims(snapshot) {
    const data = snapshot.data()
    console.log('New claims doc\n', data)
    if (data._synced) {
        if (synced &&
              !data._synced.isEqual(synced)) {
            // Force a refresh of the user's ID token
            console.log('Refreshing token')
            currentUser.getIdToken(true)
        }
        synced = data._synced
    }
}

      if (user) {
        try {
          const idTokenResult = await user.getIdTokenResult();
          setCurrentUser({...user, role: idTokenResult.claims.role, isAdmin: idTokenResult.claims.isAdmin, group: idTokenResult.claims.group });
          setError(undefined);
          listenToClaims();
          reportAdminStatus();
        } catch (e) {
          setError(e);
        }
      } else {
        setCurrentUser(undefined);
      }
    });
      return [currentUser, error]
      
      
  }, []);

  return (
    <AuthContext.Provider value={{ currentUser }}>
      {children}
    </AuthContext.Provider>
  )
}
  • Setting state is an asynchronous operation in React (see https://stackoverflow.com/questions/54119678/is-usestate-synchronou and https://stackoverflow.com/a/53914175) , so you can't immediately make calls on the user after that. Why don't you just pass `user` into `reportAdminStatus`? – Frank van Puffelen Apr 08 '21 at 22:52
  • Hi Frank. Thanks for your reply. I was hoping for some more specific guidance. So I took the reportAdminStatus out completely and still was getting an issue when checking for new claims on my firestore. It must have to do with the order. Or...do I have to put them into two useEffects? I am basically trying to implement part of this article which is in typescript. You can see here as soon as the Claims are changed in the firestore doc....it updates the firebase auth using a cloud function. This is then ready for request by the client.I need to get the client to update the token realtime. – showmeyourskills Apr 08 '21 at 23:49
  • If you put the code from `reportAdminStatus ` inside your `auth.onIdTokenChanged(user` listener, what does it log? – Frank van Puffelen Apr 08 '21 at 23:52

0 Answers0