我们在工作中的 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 令牌刷新,并将用户重定向回请求的页面。是这样的:
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
当然,如果令牌过期,会延迟用户的第一次请求,但它可以确保令牌有效,而不会强制用户再次登录。
我最近遇到了同样的问题,并通过创建一个在给定时间间隔后刷新令牌的服务找到了解决方法
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);
}