Coding in Flow logoGet my free React Best Practices mini course
← Blog

Self-Rolled vs Authentication Providers for Your Next Website

Aug 1, 2023 by Florian

Featured image

In this post, I want to talk about adding authentication to your web projects, the pros and cons of different approaches, and which one I prefer.

Table of Contents

Categories of authentication solutions

Authentication is necessary to add user login and user accounts to your app or website. Whenever you want to tie data in your database to a specific user, you need authentication. For example, a private note-taking app where users can store notes in their account that no other user should see.

I think authentication solutions today can be separated broadly into 3 categories:

  1. "Self-rolled", meaning that you implement many of the complex steps involved yourself.
  2. Libraries that handle a lot of complexity for you but still allow you to handle your user data yourself.
  3. Fully-blown authentication providers that not only simplify the authentication steps but also store and manage your user data for you.

I haven't tried every authentication library and provider out there, but I tried out some in each category. Let's talk about their pros and cons.

Self-rolled authentication

By "rolling your own" authentication I understand skipping libraries that handle authentication mechanisms for you and instead implementing this logic yourself.

The problem with this approach is that bugs in your authentication code can have dramatic consequences. There are plenty of stories of companies that accidentally exposed their user's sensitive data and even got sued for it.

Of course, there are different levels to which you can take this approach. In my project, for instance, I store the encrypted user passwords in my own database. I didn't write the encryption logic, however, but instead use the bcrypt library for that. This way, the most critical (and most complex) part is still handled by third-party code written by people who know what they're doing. My responsibility is to make sure passwords actually get encrypted and that I don't make any other sensitive user data publicly visible.

If you want to see an example of this, check out my MERN beginner course on Youtube where we hash and store passwords in our own database.

Similarly, I use Google's own server authentication library on TutHub to handle the Google login flow. This still requires me to create a bunch of different endpoints, create verification tokens and pass arguments back and forth, but the library removes some of the work.

When I wrote TutHub, I was still fairly new to web development. Today, I would use an authentication library that handles more of these steps for me.

Authentication libraries

By "authentication libraries" I mean libraries that abstract away even more of the authentication steps, but still require you to handle your own user data.

Two examples of this are Next-Auth (for Next.js) and Passport.js (for Express). They are both pretty similar in what they do.

These authentication libraries still require (or rather allow) you to store and manage your user's data. You still have to encrypt and store passwords in your own database when using Passport.js, and Next-Auth actually disallows storing passwords in your DB completely and instead encourages you to use safer, password-less alternatives like social login or magic email links.

Where these libraries really shine is when you add multiple different authentication methods, especially different social login providers (like Google, Facebook, GitHub, etc.). Both Next-Auth and Passport.js:

  • Help you store your user data in a unified way, by processing and normalizing the data coming from the different social providers.
  • Make social login flows much easier, by adding the required endpoint handlers.
  • Make the authentication flow more secure by automatically implementing best practices like adding the OAuth2 state parameter.

These libraries usually work with any database, no matter if SQL or NoSQL.

Next-Auth wraps both the frontend and backend (because Next.js is a full-stack framework) and also protects you from accidentally exposing sensitive user information, by only adding a few necessary fields to the session object by default. In a fully self-rolled auth, you have to manage (and remember to do) this yourself.

One thing that should be noted is that these libraries are only really easy to use when using database sessions and not JWTs (JSON Web Tokens). Database sessions are stored in your database and retrieved every time the user interacts with your server. This makes it easy to always have the latest user information and also to withdraw access from a user, for example when their account has been hacked. JWTs are stateless and can be more scalable than sessions in large apps, but they need a more sophisticated handling strategy in order to make them actually secure. Next-Auth and Passport don't handle this for you.

If you need features like email verification or magic links, you have to sign up for a separate email service. Next-Auth helps you with creating and sending these emails. Note that you don't need emails for authentication if you only use social login providers (and social accounts are usually already verified).

If you want to learn how to use Next-Auth, check out my Next.js e-commerce tutorial on Youtube. If you want to learn Passport.js, we use that in my Next.js with Express course, which is paid. However, you can find the full source code in this repo.

Third-party authentication providers

If you want even more of the authentication steps handled for you, you can use a third-party authentication provider. Popular examples that I've tried out myself are Firebase, Supabase, and Clerk.

Firebase and Supabase are complete backend services that also include databases, storage buckets, and other features besides just authentication. Clerk, on the other hand, is a pure authentication provider, specializing only around that.

Compared to authentication libraries, these services can handle additional steps for you, including:

  • Store user data (including passwords) in the provider's database and make sure it's always handled securely.
  • Send transactional emails (and even SMS), on your behalf (like email confirmation or password reset).
  • Make session management easier and more efficient, by automatically handling JWT access and refresh tokens, and by adding additional session management features.

A Clerk Auth user management dialog

In the image above, you can see a user management dialog that Clerk provides out of the box. It even shows the user's active devices which is difficult to implement yourself.

However, there is a price to pay for this additional convenience. Literally, in the sense that these services cost money. They usually provide a free tier and then charge for additional users once you exceed that.

Supabase, at the time of writing this, includes 50k monthly active users (MAU) in their free tier. That's very attractive for new projects. The Pro plan costs $25/month and gives you 100k MAU. However, once you exceed that, you pay $0.00325 per additional MAU, which is $325 for every additional 100k monthly active users. That's a different ballpark and can become costly very quickly.

Clerk's Business plan only includes 1000 monthly active users, with $0.05 for each additional monthly active user. This really only is feasible if your app doesn't include a free tier and every user is a paying user.

But you also have to pay a price in terms of flexibility. These authentication providers store your user's data for you, which can make it harder to store and retrieve additional user information or make modifications to the user table. I remember running into some issues when using Supabase because of how closed-off the user table was from the rest of the database.

You should also make sure that the service allows you to export your users' data, should you ever need to. From my knowledge, Firebase, Supabase, and Clerk all allow you to do that. Still, it's better to pick the correct authentication solution when you start building your project rather then when you run into bottlenecks.

Which authentication solution should you pick?

My preference is to use an authentication library like Next-Auth (if your backend is built in Next.js) or Passport.js (if you use Express). They remove a lot of the complicated work (especially in handling social login providers) while still allowing you to fully own your user data and store it wherever, and however you want.

If a fully-blown authentication service like Supabase or Clerk makes sense, is something you have to decide for each project individually. If you want to get a small project up and running quickly, and you don't expect to get hundreds of thousands of active users, Supabase can replace your whole backend including authentication for free or a small fee. If you create a paid service where every user pays money, then Clerk could be an even better option because of its extensive user management features. By the way, I'm currently working on a tutorial where we use Clerk as the authentication provider. So keep an eye open on my Youtube channel. I'm sure one day I'll also make one for Supabase.

I hope this little comparison helped you and makes the decision between auth options for your future projects easier.

Again, if you want to learn how to implement different authentication strategies in practice, check my tutorials on Youtube. And if you want more byte-sized coding advice, follow Coding in Flow on Twitter!

See you in the next one!

Happy coding and take care!


Tip: I send regular high-quality web development content to my free email newsletter. It's a great way to improve your skills and stay ahead of the curve.

Subscribe Now

Check out these other posts: