我正在开发一个使用 Next.js 13 和带有 id 令牌的 firebase auth 的应用程序。
我想利用服务器端组件的 Next.JS 内置功能来更快地获取用户数据,因此我需要在初始请求时验证服务器上的 id 令牌。当没有用户登录受保护的路由时,我想重定向到登录页面。
当用户处于非活动状态超过 1 小时并且 id 令牌已过期时,就会出现问题。下一个请求标头将发送过期的令牌,导致auth.verifyIdToken
拒绝它。这将在任何客户端代码有机会运行之前将用户重定向到登录页面,包括user.getIdToken
刷新令牌。
有没有办法在服务器端刷新 id 令牌?我读here https://stackoverflow.com/questions/73686177/how-to-refresh-firebase-token-from-back-end-using-node-js/73686213?noredirect=1#comment132982792_73686213,有一个使用 firebase REST API 的解决方法,这似乎不安全。
Context
I use the `firebaseui` [package][2] 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>
);
};
);
};
服务器端根组件
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,
};
};