英文:
refresh firebase id token server-side
问题
I am working on an app with Next.js 13 and firebase auth with id tokens.
我正在开发一个使用Next.js 13和Firebase身份验证的应用,使用ID令牌进行身份验证。
I want to leverage Next.JS built-in capability for server-side components to fetch user data faster, therefore I need to verify id tokens on the server at initial request. When no user is logged in on protected routes, I want to redirect to the login page.
我希望利用Next.JS内置的服务器端组件获取用户数据的能力,因此我需要在服务器上验证ID令牌以满足初始请求。在受保护的路由上没有用户登录时,我希望重定向到登录页面。
The problem arises when the user was inactive for >1h and the id token has expired. The next request header will send the expired token causing auth.verifyIdToken
to reject it. This will redirect the user to the login page, before any client-side code had a chance to run, including user.getIdToken
to refresh the token.
当用户闲置超过1小时并且ID令牌已过期时,问题就出现了。下一个请求标头将发送已过期的令牌,导致auth.verifyIdToken
拒绝它。这会在任何客户端代码有机会运行之前将用户重定向到登录页面,包括user.getIdToken
来刷新令牌。
Is there a way to refresh the id token on the server-side? I read here, that there is a work-around using Firebase REST API, which seems insecure.
有没有办法在服务器端刷新ID令牌?我在这里读到,有一种使用Firebase REST API的解决方法,但似乎不够安全。
Context
上下文
I use the firebaseui
package for login, which creates the initial id token & refresh token. Then I have an AuthContextProvider
to provide & refresh the id token on the client:
我使用firebaseui
包进行登录,它创建了初始的ID令牌和刷新令牌。然后我有一个AuthContextProvider
在客户端上提供和刷新ID令牌:
const ServerAuthContextProvider = ({
children,
user,
cookie,
}: {
children: ReactNode;
user: UserRecord;
cookie: Cookie;
}) => {
useEffect(() => {
if (typeof window !== "undefined") {
(window as any).cookie = cookie;
}
return auth.onIdTokenChanged(async (snap) => {
if (!snap) {
cookie.remove("__session");
cookie.set("__session", "", { path: "/" });
return;
}
const token = await snap.getIdToken();
cookie.remove("__session");
cookie.set("__session", token, { path: "/" });
});
}, [cookie]);
return (
<serverAuthContext.Provider
value={{
user,
auth,
}}
>
{children}
</serverAuthContext.Provider>
);
};
服务器端根组件
const RootLayout = async ({ children }: { children: React.ReactNode }) => {
const { user } = await verifyAuthToken();
if (!user) redirect("/login");
return (
<html lang="en">
<body>
<ServerAuthContextProvider user={user}>
{children}
</ServerAuthContextProvider>
</body>
</html>
);
};
服务器端令牌验证
const verifyAuthToken = async () => {
const auth = getAuth(firebaseAdmin);
try {
const session = cookies().get("__session");
if (session?.value) {
console.log("found token");
const token = await auth.verifyIdToken(session.value);
const { uid } = token;
console.log("uid found: ", uid);
const user = await auth.getUser(uid);
return {
auth,
user,
};
}
} catch (error: unknown) {
if (typeof error === "string") {
console.log("error", error);
return {
auth,
error,
};
} else if (error instanceof Error) {
console.log("error", error.message);
return {
auth,
error: error.message,
};
}
}
return {
auth,
};
};
英文:
I am working on an app with Next.js 13 and firebase auth with id tokens.
I want to leverage Next.JS built-in capability for server-side components to fetch user data faster, therefore I need to verify id tokens on the server at initial request. When no user is logged in on protected routes, I want to redirect to login page.
The problem arises when the user was inactive for >1h and the id token has expired. The next request header will send the expired token causing auth.verifyIdToken
to reject it. This will redirect the user to login page, before any client-side code had a chance to run, including user.getIdToken
to refresh the token.
Is there a way to refresh the id token on server-side? I read here, that there is a work-around using firebase REST API, which seems insecure.
<h2>Context</h2>
I use the firebaseui
package for login, which creates the initial id token & refresh token. Then I have an AuthContextProvider
to provide & refresh the id token on the client:
const ServerAuthContextProvider = ({
children,
user,
cookie,
}: {
children: ReactNode;
user: UserRecord;
cookie: Cookie;
}) => {
useEffect(() => {
if (typeof window !== "undefined") {
(window as any).cookie = cookie;
}
return auth.onIdTokenChanged(async (snap) => {
if (!snap) {
cookie.remove("__session");
cookie.set("__session", "", { path: "/" });
return;
}
const token = await snap.getIdToken();
cookie.remove("__session");
cookie.set("__session", token, { path: "/" });
});
}, [cookie]);
return (
<serverAuthContext.Provider
value={{
user,
auth,
}}
>
{children}
</serverAuthContext.Provider>
);
};
);
};
server-side root component
const RootLayout = async ({ children }: { children: React.ReactNode }) => {
const { user } = await verifyAuthToken();
if (!user) redirect("/login");
return (
<html lang="en">
<body>
<ServerAuthContextProvider user={user}>
{children}
</ServerAuthContextProvider>
</body>
</html>
);
};
server-side token verification
const verifyAuthToken = async () => {
const auth = getAuth(firebaseAdmin);
try {
const session = cookies().get("__session");
if (session?.value) {
console.log("found token");
const token = await auth.verifyIdToken(session.value);
const { uid } = token;
console.log("uid found: ", uid);
const user = await auth.getUser(uid);
return {
auth,
user,
};
}
} catch (error: unknown) {
if (typeof error === "string") {
console.log("error", error);
return {
auth,
error,
};
} else if (error instanceof Error) {
console.log("error", error.message);
return {
auth,
error: error.message,
};
}
}
return {
auth,
};
};
答案1
得分: 3
对于 SSR 应用程序,你应该使用会话 cookie。与 ID 令牌不同,它们不会在 1 小时内过期,你可以将它们配置为在最多 14 天内保持活动。<br/>当用户使用 Firebase 客户端 SDK 登录时,你可以使用 getIdToken()
一次,并将其传递给一个生成并设置会话 cookie 的 API,该会话 cookie 可以在后续请求中读取。这样你就不必使用监听器来更新令牌。
请确保你在客户端 SDK 中将用户注销,要么通过将身份验证持久性设置为 none
,要么显式注销用户,以便在清除他们的 cookie(使用注销 API)时,客户端没有用户。
英文:
For SSR applications, you should use session cookies. Unlike ID Tokens, they do not expire in 1 hour and you can configure them to be active for up to 14 days. <br/>When a user logs in with Firebase client SDK, you can use getIdToken()
once and pass to it an API that generates and sets a session cookie that can be read in subsequent requests.
This way you don't have keep the token updated using a listener.
Do make sure you log the user out from the client SDK either by settings auth persistence to none
or explicitly logging out the user so in case you clear they cookie (using a logout API) there is no user on client side.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论