-
Notifications
You must be signed in to change notification settings - Fork 2.7k
docs(examples): add Firebase Auth + Firestore example #1811
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Hi @ccssmnn, Welcome, and thank you for contributing to Remix! Before we consider your pull request, we ask that you sign our Contributor License Agreement (CLA). We require this only once. You may review the CLA and sign it by adding your name to contributors.yml. Once the CLA is signed, the If you have already signed the CLA and received this response in error, or if you have any questions, please contact us at [email protected]. Thanks! - The Remix team |
|
Thank you for signing the Contributor License Agreement. Let's get this merged! 🥳 |
|
@davideast Might be a good one for you to review :) |
|
Should the client sdk be used on the server? Saw this discussion on twitter where they say it should be called on the client and pass the idToken on the action. |
|
I already mentioned that in another comment here. I personally don't think it's the best approach to use the client sdk on the server since state is stored within the sdk so I don't know if it'll cause any adverse side effects. |
|
Just so long as you're not unnecessarily using the sdk on the client. I haven't had a chance to review this yet, but I've successfully gotten firebase working with Remix with no client-side code required and that's what I'd want for the example if possible. |
|
@kentcdodds I'm still playing with my implementation of firebase/remix. I've seen one of your youtube videos on firebase, but if you have others I can try and test some other approaches as well. |
|
There is more to Firebase where things might need to be done differently when doing them the Remix way:
I think creating one example for each of these tasks will bloat the example directory. One well-structured example for all things Remix+Firebase would be wonderful. We could enhance this example, once nice approaches for these features are found. |
I need to work on a few of those items over the next month.
If you have a way you would like to coordinate the effort please let me know. Otherwise, I will keep checking here and update you with any progress. |
|
I'm fine having one sizeable example that's the one-stop example for all things firebase 👍 |
| const { user } = await signInWithEmailAndPassword( | ||
| auth.client, | ||
| email, | ||
| password | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's safe to use the firebase client sdk on server-side. It is a stateful lib that has the concept of a "current user".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From the Firebase Docs on the client SDK:
Follow this guide to use the Firebase JavaScript SDK in your web app or as a client for end-user access, for example, in a Node.js desktop or IoT application.
Of course, you can mess up your server implementation. I don't yet see why receiving the user directly from the sign-in or verify-token calls should be unsafe. See this comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW I've been dealing with this recently myself. I'm not sure if what I did is correct, but I decided to handle the authentication purely on the client, then I POST the token to the server where it is verified and then set the session cookie. I previously did the auth on the server like this example, but then I could not use the federated logins (like Apple, Facebook, Google, etc). Handling it on the client side was the only way I could get the federated logins to work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's totally fine to use Firebase on the client and send the JWT through a cookie for the following requests. This example is to show how to use Firebase in your Remix app without using Firebase on the client. From there on you can enhance with client side js
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Follow this guide to use the Firebase JavaScript SDK in your web app or as a client for end-user access, for example, in a Node.js desktop or IoT application.
for end-user access
i.e. not for a server handling requests for multiple users
in a Node.js desktop
i.e. where there would only be a single user at a time
IoT application
Similarly, I think they are inferring where you would have a single user at a time.
As @jadlr mentioned, the Firebase client SDK is stateful. When you sign in, this user remains signed in for all requests until the next sign in attempt. If you were to use firebase/firestore server side, I'm pretty sure it would act as whoever the last person to sign in was.
Sure, it is likely possible to avoid revealing security holes to end users by limiting access to the API (e.g. by only using the sign in API, not using firebase/firestore, and not revealing tokens/server state to users), but I don't think it's a good idea to put this out there as a recommended approach without confirmation from the Firebase team that the client SDK is built and tested for this purpose.
See this comment.
This is a comment saying that they use it in integration tests. I'm not convinced this means it has been built and tested for the purpose of a multi user enviroment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm in the process of integrating Firebase auth in to a Remix app via the Firebase Auth and Firestore REST APIs. I'll try report back on how I get on. I have this working via the Firebase emulator but not in production. I saw issues with rate limiting being mentioned on Twitter which I think would apply to both the REST and client SDK approaches.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@penx thank you for investigating this further. Indeed, the firebase sdk's don't seem to be intended to be used this way, which may or may not cause risks using this in production. I didn't have the time to investigate the REST APIs. I'm really looking forward to seeing the results of your investigations!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have auth working with the Firebase REST API now. I plan to work on some Firebase related PRs for Remix in the coming weeks, including updating the example to use the REST API.
I think I've also figured a way to get around the rate limiting when used server side so will include that if it works. As far as I'm aware the rate limiting effects both the REST API and the client side SDK when used from the same IP.
My plan is to authenticate on the client via the REST API if the JavaScript has loaded, altering the body sent to the action in the process, and fall back to doing it in the action JavaScript has not loaded or failed. Then when the action received a request, it would either:
- contain username/password => fallback to server side login via rest API
- contain response from Firebase after a client side login => server side validate the session before saving in to Remix session (as validation would not be subject to rate limiting)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ccssmnn I opened a few PRs today:
- fix(examples/firebase-auth-firestore): Fix imports and throw meaningful errors when missing .env #3353
- feat(examples/firebase-auth-firestore): use emulators during development #3356
- fix(examples): use
postaction infirebase-auth-firestoreexample #3358 - feat(examples/firebase-auth-firestore): use rest api for server auth #3362
And have another ready to go when #3362 is merged, where I implement client-side REST auth:
|
This PR has the needs-response label. What is expected is not clear to me, though. I think, that a full Firebase example needs more Firebase features covered as described here This can still be a useful example for people interested in using Firebase and Remix together. |
|
Sounds good to me 👍 Thanks for following up! |
|
This was SUPER helpful. Thanks everyone! One small change I had to make was to destructure the imports in import {
getApps as getServerApps,
initializeApp as initializeServerApp,
cert as serverCert,
} from 'firebase-admin/app'
import { getApps as getClientApps, initializeApp as initializeClientApp } from 'firebase/app'
import { getAuth as getServerAuth } from 'firebase-admin/auth'
import { getAuth as getClientAuth } from 'firebase/auth'
if (getClientApps().length === 0) {
initializeClientApp(JSON.parse(process.env.CLIENT_CONFIG as string))
}
if (getServerApps().length === 0) {
initializeServerApp({
credential: serverCert(JSON.parse(process.env.SERVICE_ACCOUNT as string)),
})
}
export const auth = {
server: getServerAuth(),
client: getClientAuth(),
} |
Co-authored-by: Michaël De Boey <[email protected]>
|
@mocon I was not able to get the Remix Firebase Auth example to work until I applied your update to |
Apply fixes as per comment by @mocon at remix-run#1811 (comment) Also: - throw meaningul errors when environment variables are missing or can't be parsed. - check for existence of environment variable rather than cast to string
Apply fixes as per comment by @mocon at remix-run#1811 (comment) Also: - throw meaningul errors when environment variables are missing or can't be parsed. - check for existence of environment variable rather than cast to string
Apply fixes as per comment by @mocon at remix-run#1811 (comment) Also: - throw meaningul errors when environment variables are missing or can't be parsed. - check for existence of environment variable rather than cast to string
Use the Firebase REST API for authenticating server side. The client SDK is stateful. It _may_ not pose a security risk if it is _only_ used for authentication calls, but could lead to severe security issues if users were to also address other libraries such as Firestore through the client SDK. The Firebase Auth REST API gives us equivalent functionality. As far as I'm aware, there are rate limiting issues with both approaches, which I plan to address in a follow up PR. This was previously discussed at remix-run#1811 (comment)
Use the Firebase REST API for authenticating server side. The client SDK is stateful. It _may_ not pose a security risk if it is _only_ used for authentication calls, but could lead to severe security issues if users were to also address other libraries such as Firestore through the client SDK. The Firebase Auth REST API gives us equivalent functionality. As far as I'm aware, there are rate limiting issues with both approaches, which I plan to address in a follow up PR. This was previously discussed at remix-run#1811 (comment)
Use the Firebase REST API for authenticating server side. The client SDK is stateful. It _may_ not pose a security risk if it is _only_ used for authentication calls, but could lead to severe security issues if users were to also address other libraries such as Firestore through the client SDK. The Firebase Auth REST API gives us equivalent functionality. As far as I'm aware, there are rate limiting issues with both approaches, which I plan to address in a follow up PR. This was previously discussed at remix-run#1811 (comment)
This PR adds a Firebase example with Auth-flow and minimal to-do functionality based on Firestore.
app/server/firebase.server.tsinitializes the client & server SDKsapp/server/auth.server.tsimplements auth utilities to sign-in users or to protect routes and actionsapp/server/db.server.tsshows how to utilize FirestoreDataConverters to have TypeScript support when dealing with Firestore