如何协调 Firebase Auth 令牌刷新与 Server-Side 呈现

How to reconcile Firebase Auth token refreshing with Server-Side Rendering

我们在 Next.js 工作应用中使用 Firebase。我对两者都是新手,但我尽力阅读了两者。我的问题更多是关于 Firebase,而不是 Next.js。这是上下文:

我找不到 server-side 等价于 onIDTokenChanged

blog post mentions a google API endpoint刷新一个令牌。我可以从服务器上访问它并给它一个刷新令牌,但感觉就像我完全走出了 Firebase 领域,我担心维护 ad-hoc 系统会是一个负担。

所以我的问题是,人们通常如何协调 Firebase 身份验证与 SSR?我错过了什么吗?

谢谢!

我最近也遇到了同样的问题,自己解决了。我创建了一个非常简单的页面,负责强制刷新 firebase 令牌,并将用户重定向回请求的页面。是这样的:

  • 在 server-side 上,从 cookie 中提取后检查令牌 exp 值(如果您在该服务器上使用 firebase-admin,它可能会告诉您错误验证后)
// Could be a handler like this
const handleTokenCookie = (context) => {
  try {
    const token = parseTokenFromCookie(context.req.headers.cookie)
    await verifyToken(token)
  } catch (err) {
    if (err.name === 'TokenExpired') {
      // If expired, user will be redirected to /refresh page, which will force a client-side
      // token refresh, and then redirect user back to the desired page
      const encodedPath = encodeURIComponent(context.req.url)
      context.res.writeHead(302, {
        // Note that encoding avoids URI problems, and `req.url` will also
        // keep any query params intact
        Location: `/refresh?redirect=${encodedPath}`
      })
      context.res.end()
    } else {
      // Other authorization errors...
    }
  }
}

这个处理程序可以在 /pages 上使用,就像这样

// /pages/any-page.js
export async function getServerSideProps (context) {
  const token = await handleTokenCookie(context)
  if (!token) {
    // Token is invalid! User is being redirected to /refresh page
    return {}
  }

  // Your code...
}
  • 现在您需要创建一个简单的 /refresh 页面,负责在 client-side 上强制刷新 firebase 令牌,并且在更新令牌和 cookie 后,它应该将用户重定向回所需的页面.
// /pages/refresh.js

const Refresh = () => {
  // This hook is something like https://github.com/vercel/next.js/blob/canary/examples/with-firebase-authentication/utils/auth/useUser.js
  const { user } = useUser()
  React.useEffect(function forceTokenRefresh () {
    // You should also handle the case where currentUser is still being loaded

    currentUser
      .getIdToken(true) // true will force token refresh
      .then(() => {
        // Updates user cookie
        setUserCookie(currentUser)

        // Redirect back to where it was
        const decodedPath = window.decodeURIComponent(Router.query.redirect)
        Router.replace(decodedPath)
      })
      .catch(() => {
        // If any error happens on refresh, redirect to home
        Router.replace('/')
      })
  }, [currentUser])

  return (
    // Show a simple loading while refreshing token?
    <LoadingComponent />
  )
}

export default Refresh

当然如果token过期会延迟用户的第一次请求,但它保证了token有效,不会强制用户重新登录。