You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Firebase Auth时如何自动刷新过期idToken 无需浏览器全量刷新页面

解决方案

你可以通过Axios响应拦截器+Firebase内置的token刷新能力实现无感知刷新idToken,完全不需要页面刷新,具体实现步骤如下:


第一步:封装通用token获取方法

Firebase 提供的 getIdToken() 方法本身已经内置了过期判断逻辑,调用时会自动识别当前token是否过期,过期的话会自动向Firebase服务请求新token,不需要你手动处理刷新逻辑。先封装一个通用工具函数,方便后续在任意位置调用:

// 新建utils/auth.ts
import firebase from '../config/firebase'
import nookies from 'nookies'

// 获取当前有效idToken
export const getLatestIdToken = async () => {
  const currentUser = firebase.auth().currentUser
  if (!currentUser) return null
  return await currentUser.getIdToken()
}

// 强制刷新idToken
export const forceRefreshIdToken = async () => {
  const currentUser = firebase.auth().currentUser
  if (!currentUser) return null
  const newToken = await currentUser.getIdToken(true)
  nookies.set(undefined, 'idk', newToken, { path: '/' })
  return newToken
}

然后把方法暴露到AuthContext中,方便组件内调用:

// AuthContext.tsx 改动部分
import { getLatestIdToken, forceRefreshIdToken } from '../utils/auth'

type AuthContextType = {
  user: AuthUser | null
  login: (email: string, password: string) => Promise<any>
  signup: (email: string, password: string) => Promise<any>
  logout: () => Promise<any>
  oAuthLogin: (provider: string) => Promise<any>
  getLatestIdToken: () => Promise<string | null>
  forceRefreshIdToken: () => Promise<string | null>
}

// 在AuthContextProvider里添加方法实现
const returnObj = {
  user,
  login,
  signup,
  logout,
  oAuthLogin,
  getLatestIdToken,
  forceRefreshIdToken
}

第二步:添加Axios响应拦截器,自动处理401场景

在你的axios配置文件里添加拦截器,当请求返回401状态码的时候,先尝试刷新token,然后用新的token重发原来的请求,用户完全感知不到这个过程:

// axios配置文件
import axios from 'axios'
import { forceRefreshIdToken } from '../utils/auth'
import nookies from 'nookies'

let isRefreshing = false
let failedRequestsQueue: Array<(token: string) => void> = []

axios.interceptors.response.use(
  (response) => response,
  async (error) => {
    const originalRequest = error.config
    // 只处理鉴权失败且不是重发的请求
    if (error.response?.status === 401 && !originalRequest._retry) {
      if (isRefreshing) {
        // 如果已经在刷新token,把当前请求加入队列等待刷新完成
        return new Promise((resolve) => {
          failedRequestsQueue.push((newToken: string) => {
            originalRequest.headers.Authorization = `Bearer ${newToken}`
            resolve(axios(originalRequest))
          })
        })
      }

      originalRequest._retry = true
      isRefreshing = true

      try {
        const newToken = await forceRefreshIdToken()
        if (!newToken) {
          // 拿不到新token说明登录状态已经失效,跳转到登录页
          window.location.href = '/login'
          return Promise.reject(error)
        }
        // 更新全局默认请求头
        axios.defaults.headers.common.Authorization = `Bearer ${newToken}`
        originalRequest.headers.Authorization = `Bearer ${newToken}`
        // 执行队列中所有待重发的请求
        failedRequestsQueue.forEach((callback) => callback(newToken))
        failedRequestsQueue = []
        // 重发当前请求
        return axios(originalRequest)
      } catch (err) {
        // 刷新失败,清空登录状态跳登录
        await firebase.auth().signOut()
        nookies.destroy(undefined, 'idk')
        window.location.href = '/login'
        return Promise.reject(err)
      } finally {
        isRefreshing = false
      }
    }
    return Promise.reject(error)
  }
)

可选优化:主动定时刷新token

如果你想完全避免出现401的情况,可以在AuthContext里加定时器,提前5分钟主动刷新token:

// AuthContextProvider 内新增useEffect
useEffect(() => {
  if (!user) return
  // 每55分钟刷新一次,比1小时有效期早5分钟
  const refreshTimer = setInterval(async () => {
    await forceRefreshIdToken()
    setUser(prev => prev ? {...prev, idToken: newToken} : null)
  }, 55 * 60 * 1000)
  return () => clearInterval(refreshTimer)
}, [user])

注意

  • 不需要修改你现有Express端的auth中间件,原有逻辑完全可以复用
  • 拦截器里的队列逻辑是为了处理同时发起多个请求的场景,避免重复刷新多次token

内容的提问来源于stack exchange,提问作者sairaj

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 15:12:03