首页 > 解决方案 > Firebase:如果currentUser为null,如何在http请求上刷新idToken

问题描述

我目前正在使用带有 Firebase 的 React JS 来处理登录操作。

我和团队决定使用自定义身份验证系统方式进行登录。因此,我们从后端生成自定义令牌,然后前端通过 signInWithCustomToken 执行登录。

直到这里,一切都像魅力一样工作,但是一旦我登录,如果我刷新页面,currentUser 对象将变为 null,因此我无法在其上调用方法 getIdToken(true) 来刷新令牌。每次我使用 axios 发出 http 请求时都会调用 firebase.auth().currentUser.getIdToken(true) 方法(使用拦截器将 Authorization 字段添加到请求中)。如果我登录并在不重新加载的情况下发出 http 请求,它可以正常工作,但如果我刷新我会收到错误,因为 currentUser 对象会丢失其内容并变为空。

我花了或多或少 8 个小时在 Internet 上冲浪寻找解决方案。我已经看到有人说解决方案是将用户带入 onAuthStateChanged观察者,但问题是我在发出 http 请求时无法进入观察者以获取用户的值,因为观察者不是函数我可以在我需要的时候打电话。

我还尝试将 onAuthStateChanged 观察者在触发时(在登录或注销期间)返回的用户对象保存在 localStorage 上,但是用户对象与返回的用户对象不同,并且它在原型中没有getIdToken 函数。所以对我来说没用。

我也尝试使用 firebase.auth().setPersistence(firebase.auth.Auth.Persistence.SESSION),将 onAuthStateChanged 观察者返回的用户对象传递给它,但没有任何改变。

这是我的项目的代码:

****** AXIOS 请求拦截器 ******

instanceAXIOS.interceptors.request.use(
  async config => {
    const { refreshedToken } = await refreshUserTokenTEST()
    config.headers.Authorization = `Bearer ${ refreshedToken }`
    return config
  },
  error => Promise.reject(error)
)

****** 刷新令牌 ******

const refreshUserTokenTEST = () => {
  try {
    return firebase
      .auth()
      .currentUser
      .getIdToken(true)
      .then(token => {
        return { status: 200, refreshedToken: token }
      })
      .catch(err => {
        console.error(err)
        return { status: 500, refreshedToken: null }
      })
  } catch (exc) {
    console.error("ERROR DURING TOKEN REFRESH")
    return { status: 404, refreshedToken: null }
  }
}

****** firebasePersistenceOption 函数 ******

const firebasePersistenceOption = async (userObject) => {
  return firebase
    .auth()
    .setPersistence(firebase.auth.Auth.Persistence.SESSION)
    .then(function() {
      return userObject
    })
    .catch(function(error) {
      // Handle Errors here.
      var errorCode = error.code;
      var errorMessage = error.message;
    })
}

****** onAuthStateChanged 观察者 ******

firebase.auth().onAuthStateChanged(async function(user) {
  if (user) {
    console.log("LOGGED IN")
    await firebasePersistenceOption(user)
  } else {
    console.log("LOGGED OUT")
  }
})

就像我说的那样,无需重新加载页面,一切正常,因此代码中没有错误(我几乎希望是这样)。

标签: javascriptreactjsfirebasefirebase-authenticationlocal-storage

解决方案


我终于找到了解决问题的方法。感谢@DougStevenson 的建议和GitHub 上的讨论,我能够编写一个函数来获取刷新的 id 令牌。

这是基本代码:

// get the current user inside the observer
// then refresh the token
// and finally unsubscribe the observer

const getIdTokenRefreshed = async () => {
  return new Promise(async (resolve, reject) => {
    const unsubscribe = firebase
      .auth
      .onAuthStateChanged(user => {
        unsubscribe()
        const refreshedToken = await user
          .getIdToken(true)
          .catch(err => console.error(err))
        resolve(refreshedToken)
    }, reject)
  })
}

推荐阅读