如何协调 Firebase 身份验证令牌刷新与服务器端渲染

问题描述 投票:0回答:2

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

  • 在客户端应用程序中,我对 API 进行了一些调用,在

    Authorization
    标头中传递 JWT(ID 令牌)。 API 调用
    admin.auth().verifyIdToken
    来检查 ID 令牌是否足够新鲜。这工作正常,因为我或多或少保证 ID 令牌会定期刷新(通过使用
    onIDTokenChanged
    (doc link)

  • 现在我希望能够在服务器端渲染我的应用程序页面。为此,我将 ID 令牌存储在服务器可读的 cookie 中。但从现在开始,我不能保证下次用户通过整页加载来加载应用程序时 ID 令牌足够新鲜。

我找不到相当于

onIDTokenChanged
的服务器端。

这篇博客文章提到了google API端点来刷新令牌。我可以从服务器上访问它并给它一个刷新令牌,但感觉就像我完全走出了 Firebase 领域,我担心维护临时系统将成为一种负担。

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

谢谢!

firebase firebase-authentication server-side-rendering
2个回答
6
投票

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

  • 在服务器端,从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
    页面,负责强制在客户端刷新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

当然,如果令牌过期,会延迟用户的第一次请求,但它可以确保令牌有效,而不会强制用户再次登录。


0
投票

我最近遇到了同样的问题,并通过创建一个在给定时间间隔后刷新令牌的服务找到了解决方法

export const refreshToken = async () => {
  const user = auth.currentUser;

  if (user) {
    try {
      const token = await user.getIdToken(true);
      localStorage.setItem("token", token);
      store.dispatch(setToken(token));
      
      return token;
    } catch (error) {
      console.error("Failed to refresh token:", error);
    }
  } 
};

然后在你的路线中你可以做类似的事情

 if (user && token) {
          dispatch(setUser(user));
          dispatch(setToken(token));

          const refreshInterval = setInterval(() => {
            refreshToken();
          }, 15 * 60 * 1000);

          return () => clearInterval(refreshInterval);
        }
© www.soinside.com 2019 - 2024. All rights reserved.