11
5 Comments

Just discovered Firebase Auth for MERN stack and I'm never going back.

I've used Firebase Auth before for basic Firebase apps, it always worked great but I was never a fan of Firebase itself. The costs scale too quickly if you do something wrong and it just doesn't feel as "concrete" as typical client/server architecture. I could never figure out the proper rules to set up and Cloud Functions seemed slow. I always went back to my bread and butter MERN stack.

However, yesterday I made an awesome discovery. You can use standalone Firebase Auth in the MERN stack! Meaning you can use all the prebuilt Firebase authentication functionality including sign ups, adding social providers, and verification emails into your React app without needing any of the other Firebase stuff to go along. Then you can pass your Auth token to your Node server, verify it, and retrieve the user to do whatever you want in the serverside code or database! No heavy sessions, no mess with securely storing JWTs, theres built in functions for everything!

The best part is, if you don't use phone authentication, its 100% free even as it scales! (now I'm starting to sound like an ad, I'm just excited).

Some helpful tips:

https://usehooks.com/useAuth/ - Is an awesome React hook that puts all of your Firebase Auth functionality into an auth provider so anywhere in your code you can access your user or any of the sign up/sign out functionality. I just added a method for Google Auth and it was ready in seconds:

const signInWithGoogle = () => {
    const googleProvider = new firebase.auth.GoogleAuthProvider();
    firebase.auth().signInWithPopup(googleProvider).then((response) => {
        setUser(response.user);
      })
 };

In my Node/Express server I just created a simple Firebase and CheckAuth middleware like this:

// firebase.js
const admin = require('firebase-admin');
const serviceAccount = require('../../firebaseKey.json');
const firebase = admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
});
module.exports = { firebase };

// checkAuth.js
const { firebase } = require('./firebase');
const checkAuth = async (req, res, next) => {
  const authToken = req.headers.authtoken;
  if (!authToken) {
    res.status(403).send('Unauthorized');
  }
  firebase.auth().verifyIdToken(authToken).then((user) => {
      req.user = user;
      next();
    }).catch(() => {
      res.status(403).send('Unauthorized');
    });
};
module.exports = { checkAuth };

Then I can just drop checkAuth as a middleware in any route and it'll force authentication as well as provide the user on my req.

I've only been using it for a day now so maybe I'm in for a headache down the line that I haven't forseen. But I feel like it is already providing me all of the benefits of a 3rd party auth provider like Auth0, but without a lot of the headaches that came with it, and I can be in control of the design instead offloading to a hosted page.

  1. 3

    I used something very similar to MERN for years. I loved having total control of every aspect of my stack. And then I realized that I was simply wasting time writing my own shitty Firebase, my own shitty Webpack, my own shitty Gatsby/Next. Then I completely let go of everything and went 100% Firebase with Firestore, Auth, Functions, and Next or Gatsby (depending on needs) hosted on Vercel or Gatsby.com.

    I am never going back. It's just too comfortable, secure, and simple. I am building things five times faster now.

    I also replaced Redux with Recoil. I would never again recommend Redux to anyone again. Recoil is a perfect fit for React. It feels like it could be a part of React by default. It's easy to replace Redux with Recoil too, and you end up with way less code and complexity.

    As for Functions being slow, that's only the case in dev environment and during cold starts, once they warm up it's blazingly fast. I suggest you bite the bullet and try the whole stack at least once.

    Also, if you're not a fan of Firebase try Supabase.io, it's basically Firebase but open source and based on Postgres. And maybe Cloudflare Workers for ultra fast functions, they have their own key value storage solution as well. Supabase is pretty awesome but it's a bit harder to jump into.

    1. 1

      Yeah I've always been the 100% DIY type myself, just because if I didn't write the code then I was always worried it wouldn't be working as I'd expect or I'd get vendor locked and couldn't get out. But then I'd find myself having an idea and spending ~3 months just writing the boilerplate authentication, transactional emails, profile management, teams, billing, etc. and get no where on my actual product. I've been slowly offloading stuff to 3rd party services and its been making my life much easier.

      I've used the full Firebase offerings a few times, mostly through apps, and once trying to make a game, and it is definitely a great solution. Especially if I want anything "realtime" it's my go-to instead of trying to roll my own sockets. But its just too far removed from what I know that I'm always scared of introducing some security vulnerability. I think I just need to take a few more courses on it to really get to know it.

      I love Recoil! I also avoid Redux most of the time and try to get away with using a React Context to store the necessities. I'm actually trying to convince my job to switch to Recoil right now so I'm doing a deeper dive into all the state management options out there now to weigh the pros and cons.

  2. 3

    I've been using this setup for over an year now, without the React hook (I built my own code that replicates the hook functionality, basically)

    There's one issue I came up with very early on about the token refresh that happens with firebase auth every hour.
    If your/end-user's browser clock is not exactly in sync with firebase, the token expires sooner than what you might think the 'expirationTime' is and doesn't validate on the server.
    My solution was to detect a failed response when this happens, force refresh the token and send the request again.
    You can search for stackoverflow threads related to this.

    1. 1

      Interesting that makes sense since the client will read the token according to it's own clock and say it's valid but then the validation would disagree. Any tip on how to test this, like setting my clock ahead how far? Make sense to just send a retry though I'll give it a shot. Thanks for the heads up!

      Only other sticking point I've had so far is typically when I register a user there is some extra data I want to add onto an account, like a Stripe customer key, a trial start date, an account timezone, etc. At first I couldn't find how to add that to user or to make sure a separate User model on my database was synced up or created when needed. But I think I solved this by just creating an Account document on my DB that is separate from the Firebase user account. The user account is just used for authentication and then on onAuthStateChanged I try to fetch the users Account and if it doesn't exist I force them into an onboarding flow to create it. Seems to work ok so far.

      1. 1

        When I was setting this up, my local clock was somehow about 3 minutes behind compared to firebase (found out with testing). There's no way to know this for a client, so after some searching I figured retrying the request is the safest way to go with.

        To add more info for a customer, storing it in the db is the right way, unless it's related to what the user can access in the app, for which they recommend using custom claims - https://firebase.google.com/docs/auth/admin/custom-claims.

        Also make sure you know the difference between onAuthStateChanged and onIdTokenChanged.

Trending on Indie Hackers
How I grew a side project to 100k Unique Visitors in 7 days with 0 audience 49 comments Competing with Product Hunt: a month later 33 comments Why do you hate marketing? 29 comments My Top 20 Free Tools That I Use Everyday as an Indie Hacker 16 comments $15k revenues in <4 months as a solopreneur 14 comments Use Your Product 13 comments