英文:
FirebaseAuth with SvelteKit on +page.ts load
问题
我有一个SvelteKit应用程序,正在使用Firebase和Node执行简单的Google SSO身份验证。我正在使用一个需要当前登录用户的IDToken来认证请求的API。理想情况下,我想使用+page.ts
加载函数来加载数据,类似于以下方式:
export const load = (async () => {
// 获取用户和令牌
const auth = getAuth();
const user = auth.currentUser;
const token = await user?.getIDToken();
if (!token) throw error(401, "无法认证");
// 使用令牌获取加载页面所需的数据
const data = api.requestData(token);
return { data };
}) satisfies PageLoad;
export const ssr = false;
问题在于,当该函数执行时,user
始终为null。我想象这是因为这在页面加载之前调用,而Firebase还没有访问会话并获取当前用户的方式。
我的问题是,我应该采取什么方法来解决这个问题,而不仅仅在页面渲染后请求数据?是否有一种方式可以在服务器端对用户进行身份验证?非常感谢。
英文:
I have a SvelteKit app and am using Firebase and Node to do simple Google SSO auth. I am using an API that requires the IDToken of the currently signed in user to authenticate requests. Ideally I'd like to use the +page.ts
load function to load in the data, something like this:
export const load = (async () => {
// Get user, token
const auth = getAuth();
const user = auth.currentUser;
const token = await user?.getIDToken();
if (!token) throw error(401, "Could not authenticate");
// Use token to get data needed to load page
const data = api.requestData(token);
return { data };
}) satisfies PageLoad;
export const ssr = false;
The issue is that user
is always null when this function executes. I imagine this is because this is called before the page loads and Firebase hasn't had a way to access the session and get the current user.
My question is, what approach do I take to solve this without simply requesting the data after the page is rendered? Is there a way to authenticate the user server side? Thanks so much.
答案1
得分: 4
Edit (12 May '23)
以下是我之前回答中的选项1的视频链接:
https://youtu.be/8NlUTFppJkU
之前的回答
我有同样的问题,就我所知,有三种解决方案。
1. 在客户端执行所有操作
这意味着在经过身份验证的路由的根文件夹中(例如,在第一个布局文件中),进行所有身份验证操作,获取用户并将其放入一个可读存储中,然后在所有地方使用它。您可以从 Firebase 用户那里获取访问令牌。您还可以在 Firebase 完成操作时显示加载屏幕。
但是,每次页面刷新时,都必须重新进行身份验证。这类似于 Gmail 之类的应用程序。
您还需要为从 API 获取数据时的每个页面或组件提供一些加载用户体验(UX)。但这是标准的用户体验,像 YouTube 这样的应用程序做得很好。
2. 在客户端执行身份验证,但进行服务器端 API 调用
在这种情况下,您需要一个登录页面,用于对用户进行身份验证并创建会话 cookie(如 Tonton-Blax 所提到的)。然后,您将在客户端使用 Firebase 登出用户(因为您将使用 cookie)。然后,在您的 *server.js 文件中,您需要获取 cookie 来验证请求。您还需要实现另一个登出功能,以使您创建的 cookie 过期。这个选项需要更多的工作来保护您的 cookie,设置过期时间,处理 CSRF 等。
3. 放弃您的 API,完全使用 Firebase
在这种情况下,您将对用户进行身份验证,但还将导入 Firestore 以对您的数据库进行更改。如果无法直接调用您的数据库,或者例如您正在使用 Spanner,那么您需要创建执行此操作的云函数。但您可以导入您的函数并将其视为 API 端点。
我仍然不确定哪种方法更好,我想这主要取决于您的要求和您想要的用户体验。就我个人而言,出于安全性考虑,我不想处理 cookie 的麻烦,因此我选择拥有自己的 API 而不是使用云函数(在此处阅读我的决策过程)。如果您的要求还意味着您需要自己的 API,那么选项1是要走的路线,这也是我正在采取的路线。我还没有实现满意的代码,否则我会分享出来
希望这对您有帮助!
英文:
Edit (12 May '23)
Here's a video that goes through option 1 from my previous answer below:
Previous answer
I have the same problem, and there are three solutions as far as I've been able to tell.
1. Do everything client side
Meaning in your root folder for the authenticated routes (e.g. In the first layout file), do all your Auth, get the user and put in a readable store which you use everywhere. You can get the access token from the Firebase user. You can also show a loading screen whilst Firebase is doing it's thing.
But on every page refresh, the authentication has to happen again. This is similar to Gmail for example.
You'll also need some loading UX for every page or component whilst data is being fetched from your API. But this is standard UX today, and apps like YouTube do a good job of that.
2. Authenticate client side, but make serverside API calls
Here you'll need a sign-in page where you authenticate the user and create a session cookie (as Tonton-Blax mentioned). You'll also then sign out the user on the client side using Firebase (since you'll be using the cookie). Then in your *server.js files you fetch the cookie to authenticate your requests. And you'll need to implement another sign out feature which expires the cookie you've created. This option requires a bit more work to secure your cookie, set expiry, deal with CSRF, etc.
3. Ditch your API and use Firebase fully
Here you'll authenticate the user, but also import the firestore to make changes to your db. If you can't directly call your DB, or if you're using spanner for example, you'll need to create cloud functions that do that for you. But you can import your functions and treat them like API endpoints.
I'm still not sure what's the better approach, I guess it'll depend mostly on your requirements and what kind of user experience you want. Personally I don't want the hassle of dealing with cookies from a security perspective, and I chose to have my own API Vs using cloud functions (read up on my decision process here). If your requirements also mean you'll need your own API, then option 1 is the way to go - which is what I'm doing. I haven't implemented code that I'm happy with yet, otherwise I'd share
Hope this is helpful!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论