如何协调 Firebase Auth 令牌刷新与 Server-Side 呈现
How to reconcile Firebase Auth token refreshing with Server-Side Rendering
我们在 Next.js 工作应用中使用 Firebase。我对两者都是新手,但我尽力阅读了两者。我的问题更多是关于 Firebase,而不是 Next.js。这是上下文:
在客户端应用程序中,我对 API 进行了一些调用,在 Authorization
header 中传递了 JWT(ID 令牌)。 API 调用 admin.auth().verifyIdToken
来检查 ID 令牌是否足够新鲜。这很好用,因为我或多或少保证 ID 令牌会定期刷新(通过使用 onIDTokenChanged
(doc link)
现在我希望能够 Server-Side 呈现我的应用页面。为此,我将 ID 令牌存储在服务器可读的 cookie 中。但从现在开始,我无法保证下次用户通过整页加载来加载应用程序时 ID 令牌是否足够新鲜。
我找不到 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有效,不会强制用户重新登录。
我们在 Next.js 工作应用中使用 Firebase。我对两者都是新手,但我尽力阅读了两者。我的问题更多是关于 Firebase,而不是 Next.js。这是上下文:
在客户端应用程序中,我对 API 进行了一些调用,在
Authorization
header 中传递了 JWT(ID 令牌)。 API 调用admin.auth().verifyIdToken
来检查 ID 令牌是否足够新鲜。这很好用,因为我或多或少保证 ID 令牌会定期刷新(通过使用onIDTokenChanged
(doc link)现在我希望能够 Server-Side 呈现我的应用页面。为此,我将 ID 令牌存储在服务器可读的 cookie 中。但从现在开始,我无法保证下次用户通过整页加载来加载应用程序时 ID 令牌是否足够新鲜。
我找不到 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有效,不会强制用户重新登录。