Comparing Auth from Supabase, Firebase, Auth.js, Ory, Clerk and others
Looking at Auth providers from a software developer's perspective.
I’m Zsolt Ero. After reading blog posts all my life, but never writing one, I decided to start writing my thoughts while building. This is my first blog post.
I’m building a new web app and a browser extension. My previous app used server-side sessions with password login, but this time I wanted to support Single Sign-On (SSO), so I embarked on a journey to find an Auth solution.
My requirements are quite standard, or at least I thought they were:
Never log a user out unless they want to.
Support Google / GitHub Single Sign-On (SSO).
Have affordable pricing for a freemium B2C app.
As an alternative for affordable pricing, have a nice self-hosting experience.
I’ll be evaluating Auth providers from a software developer’s perspective. I’m not a security expert; I don’t have an opinion if a solution should use OAuth2 / OpenID Connect or something else. I’m looking for a setup that works reliably and gives a nice experience for my users.
Note: There is a technical distinction between authentication (AuthN) and authorization (AuthZ). Since AuthZ is super simple in my use case, I’ll be evaluating the AuthN part.
The good old setup
For MapHub, I’ve been using server-side sessions via Redis. No SSO, just password-based authentication. It’s been an extremely reliable and performant part of the infrastructure, running for almost 7 years now. Once set up, it’s been 100% stable and worry-free.
When I started writing this article, I logged into Plausible and saw that there were 19000 current visitors (Daily Mail just front-paged an article featuring a MapHub embed).
While this traffic doesn’t happen every day; I found it amazing that the single server I’ve been using for years could handle it so smoothly.
Technically, the backend is based on “boring” technologies: Pyramid (17 yr), SQLAlchemy (16 yr), PostgreSQL (26 yr) and Redis (13 yr). I believe choosing boring technology for MapHub’s backend was one of the best choices I ever made.
How about performance?
The #1 criticism about Python has always been how slow it is. The average response times during the Daily Mail Saturday evening hammer were 7-12 ms. Considering Google believes anything under 200 ms is good and 100 ms is excellent, I think these are really good numbers.
[1] curl -w “time_total” on localhost. It’s running on a dedicated AMD 3700X server.
The quest for a modern solution
7 years have passed; I am building something new and aiming to improve in these aspects:
No more mixing of templating between Python and JS: Write everything frontend related in React and make the backend JSON API only.
Support for Google / GitHub Single Sign-On (SSO).
Support for running serverless functions. Just a few endpoints for long-running functions.
And most importantly, rule #1: Do not roll my own auth. I’m looking for a stable and trusted solution made by experts in the field.
For the React + JSON API I decided to go with Next.js, but I’m trying to keep the Next.js backend part as “lite” as possible and keep using Python + SQLAlchemy for the API. (Once you’ve invested enough time to enjoy using SQLAlchemy, you don’t want to migrate to anything else.)
How did I choose which Auth solution to try out? I’ve read a lot of Hacker News and Reddit comments and tried them out in the order presented here. For some, I’ve invested 1 week+ of my time, for others, just a few hours.
Supabase
Supabase is the largest startup in this article, having received over $110 million in funding. They are making a full platform “open source Firebase alternative”, and if that’s not enough, it’s all based on PostgreSQL.
They offer amazing value: you get a managed PostgreSQL + the whole platform for $25 per month. This also includes auth for up to 100,000 monthly users, which would be hundreds or even thousands of dollars on other auth platforms.
Supabase Auth is the gateway drug to the platform. In theory, you can just use Auth, but the magic is in the interconnectedness between their systems. For example, this function inserts a new user into your tables when a user registers on Supabase Auth.
create function public.handle_new_user()
returns trigger as $$
begin
insert into public.profiles (id, full_name, avatar_url)
values (new.id, new.raw_user_meta_data->>'full_name', new.raw_user_meta_data->>'avatar_url');
return new;
end;
$$ language plpgsql security definer;
create trigger on_auth_user_created
after insert on auth.users
for each row execute procedure public.handle_new_user();
Once you’ve started using this, you’ll never leave. It solves the number one problem with auth systems, which is how to synchronize the users of the auth system with the users of your backend system. It also creates the ultimate vendor lock-in. Luckily it’s an open-source platform, so you can always go self-hosted if you want to.
Is Supabase solid?
For Auth, unfortunately, no. I’ve been constantly getting logged out randomly from my own app, so I decided to look into how they handle the auth in detail. I quickly found out it was not an isolated case: There are many reports on GitHub about other users experiencing random logouts.
Red flag 1: No setting for session lifetime
First, there is no option to set the session lifetime. This is a very important setting on every other platform with detailed documentation. For Supabase, there is no mention of it in the docs and no setting on the UI.
This is probably the most important parameter for any auth solution and is it completely missing here?
There is a single setting called “JWT expiry limit”, which confuses users, as people are setting it to the maximum value of one week, thinking it means session lifetime. In reality, it is only setting the access token’s expiry time: a limit as short as 10 seconds should still give an OK user experience, without anyone experiencing logouts.
Red flag 2: Client-side unencrypted tokens
Every information is stored client-side. This means either non-HttpOnly cookies or localStorage. The content is not encrypted, so every script that runs on your website has access to your users’ access_token, refresh_token, provider_token and provider_refresh_token in clear text.
Now, I’m not an auth expert, so I cannot evaluate how bad this behavior is, but every other solution explicitly mentions why they are not doing this.
Red flag 3: Inconsistent, undocumented behavior, no communication
Some of the official examples (for example React) use the supabase-js
package, which stores data in localStorage, while others (for example Next.js) use the auth-helpers
package, which stores data in a client-side cookie. None of this is documented anywhere.
The most important information I’m trying to figure out is what happens to the auth.sessions / refresh_tokens tables on the server.
If these are cleaned up at regular intervals, when exactly and how can we control this? This would be the missing session lifetime setting mentioned above.
If these are not cleaned up then how do people use Supabase in production? Do they really keep all those sessions / tokens forever?
I wrote on their Discord but received no answer. I kept writing my findings in a lonely thread on their GitHub Discussions, receiving no response for 1.5 months. I opened an issue directly on the auth repo, which finally received a response after a month from a dev. The official answer is that I shouldn’t worry and that “random logouts should not be happening”. Unfortunately, they do happen, which is the reason I looked into the implementation.
Red flag 4: Lack of care for Auth
When it comes to shipping new features, Supabase is one of the most impressive companies I’ve seen. Simply reading their newsletter can be overwhelming: they ship so many features so rapidly.
At the same time, I feel there is a lack of care for one of their core packages: Auth. For example, using their Next.js middleware results in a user logout. I’m not talking about a very exotic setup, this can be reproduced on their official example. However, apart from a few users trying to guess what’s going on, no one seems to care.
Red flag 100: No 2FA on their own platform
I honestly couldn’t believe it, I just thought I couldn’t find the setting. I had to ask for confirmation about it on Supabase Discord.
There is no 2FA available for app.supabase.com!
The platform, which you are supposed to trust with your business data, your customers’ private information, and your payment processor’s API keys. The platform, which received over $110 million in funding and ships a new feature every week. They don’t have 2FA in 2023.
Note: It probably also tells a lot about Supabase Auth, which offers 2FA and is used for their platform. If they are not trusting their own solution’s 2FA, then why is it even offered?
Conclusion
I came for the Auth and wanted to stay for the platform. They offer tremendous value for $25. I really wanted to like it, I’ve spent way more time trying to make it work, compared to any other solution.
Unfortunately, there are way too many red flags for me about Supabase Auth, so I decided to look for alternatives.
I understand that their Auth platform is a fork of Netlify’s gotrue and they put a lot of effort into making it work, but the end result is still not something I’d want to lock into. Now that Supabase is a big, well-funded company with some of the best developers, they could probably develop a much better auth platform from scratch. I hope they’ll do it and become an amazing platform with a solid Auth.
Note, I’m really puzzled about how the Hacker News community universally endorses Supabase. HN is known to be very critical when it comes to discussing small to big projects, even small blog posts are scrutinized, yet they give an almost universal approval for Supabase. It was the reason I picked it first and I was the most surprised when I found the above-mentioned red flags.
NextAuth.js / Auth.js
NextAuth.js is a very popular choice these days, advertising a smooth developer experience with Next.js; however, I have still decided not to go with it.
The reasons are most evident on the GitHub Contributors graph, where I can see that:
It’s a project that the original developer abandoned 2 years ago.
Another developer has picked it up and is actively working on it, doing 90% of the effort today.
The current developer’s background seems to be React and Next projects. Nothing security- or backend-related.
The other reason is that it’s not clear what they really offer. You can use a database or not. You can wrap other providers or not. You can use sessions or use JWT + JWS / JWE / JWK.
In comparison, the other solutions are full companies with a proven track record in security and they offer 1/10 of what NextAuth.js offers, developed by a single person.
I understand this is not a friendly or optimistic opinion, but choosing an Auth solution is not the same as selecting a date picker library, which you can replace in an afternoon.
I’m looking for a battle-tested auth solution developed and maintained by people who have a strong background in security. NextAuth.js didn’t pass this test for me.
Clerk
Clerk is a polished solution by a company that is 100% focused on auth. They have a solid financial background ($25 million in funding). Their website is functional and beautiful, as is their documentation.
Have a look at configuring session lifetime below: it shows exactly what’s important. A short description in the dashboard, a detailed description in the docs. Session lifetime can be set to 10 years, just what I was looking for!
They also have a super clean integration with Next.js.
Pricing and conclusion
For 10,000 active monthly users, Clerk costs $550 in the Business plan.
If you only have paying users, and they pay you at least $5 a month, this would be max 1% of your revenue. I’d gladly pay 1% for a polished auth solution.
On the other hand, if your business model is freemium, when only 1-2% of your users are on a paid plan, this could be 50% of your revenue. In this case, it’s clearly cost prohibitive.
In my opinion, Clerk’s Business plan is the nicest hosted auth solution if your business model fits their pricing. In my case it didn’t, so I continued my journey.
FusionAuth
FusionAuth is a company specializing in Auth, targeting bigger companies and Enterprises. They offer a self-hostable version of their software for free. It is not open-source, but you are allowed to run the binaries after accepting their license.
There is a Docker Compose version offered, so hosting it should be quite simple.
Once you start it, you get into a maze of settings. It is truly a maze. Just inside the “Edit Application” dialog, I’ve counted over 70 fields.
What I found great about FusionAuth is that they support both a login screen inside the app, as well as external login pages. Since I’m planning to develop a browser extension, I’d really prefer to have a custom-styled login screen inside the app, without pushing the users to a different subdomain first.
Having said that, I found absolutely no resources for building out this interface in React, nor for integrating their API in Next.js.
All in all, FusionAuth is probably a workable solution, but it would require investing way more time compared to alternatives. In summary, I would be spending a lot of time on the wrong priorities just to be able to use a closed-source software. I prefer to look for alternatives.
ZITADEL
ZITADEL is a Swiss company, just a drop from Munich-based Ory. Both of these companies seem to have a background in serving European government and Enterprise clients. Great news when it comes to security - probably these solutions are as battle tested as it gets.
On the other hand, it might not be such great news when it comes to setting up a simple Auth solution for a web app. Let’s see how ZITADEL scores.
Self-hosting experience
I have to say, without exception, their self-hosting experience is as smooth as it gets. First, they open-sourced their full solution, 1:1 identical to their hosted offering. This means the full backend and frontend, not just core components plus long evenings of tweaking config files.
Second, the hosting is so simple that it’s basically a single Docker container. You start a single container and you get a 1:1 identical offering to what you’d get in their Cloud offering.
There is an interesting issue on GitHub where the founder of ZITADEL and Ory are discussing their products’ differences. One key topic seems to be the self-hosting experience. In my finding, they couldn’t be further - to the advantage of ZITADEL.
The maze
Once it’s up and running, you find yourself in a smooth UI that contains hundreds if not thousands of settings. You have:
Organizations
Project
Applications
Roles
Grants
Authorizations
Users
Identity providers
Memberships
Actions
Flows
Domains
Instances
I’ve spent a few hours with it and I couldn’t figure out how to make a simple login screen that looks like the one on their landing page:
Setting up Single Sign-On for example looks like this. Here, I had to figure out Google’s issuer is “https://accounts.google.com” and fill in every value myself:
Here is how the same screen looks in Clerk, for example:
A few clicks vs. hours of debugging how to add each of those providers, one by one.
Login screen
ZITADEL only offers sign-up/login on an external login screen, i.e. your users will be redirected to auth.company.com, where they’ll see a page hosted by ZITADEL and they’ll be redirected back to your app after logging in. The limitation of this, of course, is that the user leaves your website for an external one that has no menubar, different design, etc., and is then redirected back.
Not a huge limitation, but I prefer to have React components and implement it inside my app, especially if flows like changing password or email need to happen on the same UI.
Next.js support
ZITADEL is one of the few solutions which doesn’t provide a Next.js library. They have an example that uses NextAuth.js where there is a “provider” for ZITADEL, but then you’ve introduced a dependency both on NextAuth.js and on ZITADEL. Twice the maintenance of keeping them updated, as well as twice the attack surface.
Case in point: simply logging in with an email & password in their example app results in 5 cookies and 1 localStorage key.
I’m not looking forward to debugging random logouts on this. It was hard enough with a single cookie on Supabase.
Conclusion
The architecture of ZITADEL is probably solid, and with enough time spent, I could probably figure out all the configuration issues. Their company was responsive, they always replied immediately on GitHub in a helpful and professional way.
If they could add a native Next.js library as well as a detailed tutorial about how to get from zero to the clean login UI like they are advertising, it could even be a winner!
For now, I decided to use different solutions.
Firebase
Firebase needs no introduction, it’s been the #1 solution for “backend-as-a-service” platforms for 11 years now. It was founded in 2012, and acquired by Google in 2014 and they have been offering its services at very affordable prices ever since. Their name is so strong that Supabase’s title on their homepage is simply “open source Firebase alternative”.
Online I’ve seen most people criticizing their Functions product, but Auth seems to be a very solid solution that is polished by now.
Client-side magic
Firebase Auth seems to be the only solution I found that offers the whole auth experience client side. It really is magical: you call a single function and auth is set up and running, including UI. You don’t have to know or understand anything about Auth or backends to make it work.
Abandoned libraries
Having said that, their React lib is abandoned and is broken on React 18. Right now, the way to use it today is by browsing GitHub issues where people are trying to figure out how to make it work. One person wrote a snippet for React and another one for Next.js, which received dozens of upvotes. You copy-paste those into your code and hope it keeps working - not exactly the developer experience you’d expect from a solid platform.
Also, I cannot get over the require() inside useEffect:
useEffect(() => {
// Firebase UI only works on the Client. So we're loading the package only after
// the component has mounted, so that this works when doing server-side rendering.
setFirebaseui(require('firebaseui')); // <----- this
}, []);
Free forever - or is it?
One of the best things about Firebase Auth is that it’s completely free for unlimited users. Probably they are the only provider which offers totally free Auth, no matter how big your project grows.
Now, if we look into the details, you can see that it’s a bit more complicated. Basically, they offer two systems, the original one “Firebase Auth” which is free, and a new one called “Firebase Auth with Identity platform”, which offers more features like MFA, Blocking functions and SAML.
Now, if you want to support a single customer needing some of this, you’d need to migrate all your users over to the new system. At which point it's going to be as expensive as other specialist auth providers.
The writing on the wall
The original Auth solution is being renamed in more and more places to “Firebase Authentication Legacy”. Now, if you open Killed By Google it doesn’t exactly give a reassuring feeling for a Google product that is already called Legacy.
I guess the legacy solution will be shut down at one point and the new “Identity platform” will become the only Firebase Auth offering. There is nothing wrong with that, but we shouldn’t think of Firebase Auth anymore as a free offering, but as a paid offering with a generous free tier (50k users!).
How do you sync user data?
Technically the biggest challenge with using Firebase Auth is how to sync your user data with your backend.
The #1 Stack Overflow answer for this question is basically “The easiest way is actually to not synchronize auth data”. Now, this might be just a single answer but catches the core idea: Firebase is really designed around the idea that you embrace Firebase as a platform, you use their DB offerings, their Functions, everything. They really don’t want you to use your own backend server with PostgreSQL and try to sync user data between the two systems.
You can find other answers which involve using Functions that trigger on Auth events, but making a reliable system based on this is not a simple task. It’d probably involve some real-time functions + a retry queue for failed events + overnight full export from Firebase with some clever diffing upon importing it.
Conclusion
Even with all the limitations, Firebase is still one of the best choices today when it comes to Auth. The abandoned React library doesn’t give a good feeling but the linked GitHub snippets work and you can get the first integration done super quickly.
Later on, it might give you headaches if you want to sync user data to your own DB, but if you are happy to use their DB it can be a solid choice.
Their free offering will probably be shut down, but their paid offering has a very generous free tier, which should cover most projects.
Supertokens
Supertokens is a relatively new startup offering hosted Auth solutions with an open-source codebase. Their self-hosting experience is really nice, there is almost nothing to configure, no web UI, just a few values. From all the solutions listed here, it might have been the simplest to get up and running.
The actual configuration happens inside your app, which seems to be a very unique solution. Here is for example the config folder of their Next.js example app.
React first
The interesting thing about Supertokens is that they especially target React developers. Their core repo has 660 commits, while their Node + React repos have almost 2000 commits together.
It has both a good and a bad side. The good side is that getting it up and running in a React codebase is literally a few clicks. They even offer their own “create-supertokens-app”, which is an interesting idea but I’d only use it to create an example app.
Implementation details all over the codebase
The bad side is that everything is super hard-wired into their way of handling React state management, which really doesn’t go well if you are using a different state management solution.
Also, there are implementation details all over your app code. For example in their official example, all the following is used in pages/:
SuperTokensReact
supertokensNode
SuperTokensWrapper
superTokensNextWrapper
Session.attemptRefreshingSession
Session.getSession
verifySession
redirectToAuth
ThirdPartyEmailPassword.signOut
pageProps.fromSupertokens === 'needs-refresh'
err.type === Session.Error.TRY_REFRESH_TOKEN
err.type === Session.Error.UNAUTHORISED
SessionAuth
useSessionContext
SuperTokens.canHandleRoute
session.getAccessTokenPayload
supertokens-node/framework/express middleware
Also, once logged in with a simple password auth, you get 5 cookies. As I said, I’m not a security expert, but 5 cookies just feel a hint too much for me.
430 kB of JS?
The very minimalistic example implementation grows the built JS size by 435 kB! That is almost half a MB of minified JS!
I have no idea why would any auth library need almost half a MB of frontend code. And if this much code is included, why are the implementation details still scattered all over the application code?
Conclusion
I might be nitpicking here, as the setup is really simple and it works and the team was very quick to respond when I asked a question from them.
It just doesn’t give me a good feeling about how a codebase would look in the future if even the simplest possible example app looks like this. For example, in a classic, server-side rendered app (say Django or Rails), this would be 100% hidden from the developers (and also extremely well-tested by now). I understand that in a Next.js app, you have to see some of it, but this is just too much for my taste.
All in all, I decided not to use Supertokens even though I got it up and running in no time.
In the article “Why you probably do not need OAuth2 / OpenID Connect” written by the founder of Ory, he writes the following about Supertokens.
“We no longer endorse them due to a questionable choice of mixing OAuth2 terminology with self-built protocols. In particular, the use of OpenID terminology without supporting the specification itself, and the use of "access" and "refresh tokens" in incorrect contexts. We believe this to be what Scott Brady calls MyOwnOAuth™.”
Ory
Ory is an exception in this list, the reason is very simple: after trying out all these solutions, I decided to go with Ory! Having said that, the journey was far from smooth sailing.
For example, by default, it cannot be used on a public-facing website, as the client sends an HTTP request to their service even for public users. I’ve managed to fix that behavior in a Next.js proxy, but there are half a dozen similar workarounds that are still needed today.
In the end, I decided to stay with Ory because their team was very professional and I believe those issues will be addressed soon. In my opinion, the core service is very solid, just the integration parts are having some rough edges.
I’ve decided to write my experience with Ory in a dedicated blog post, which I’ll be posting next. If you don’t want to miss it, you can subscribe here.
The ones I haven’t tried: Auth0, Keycloak, WorkOS
Auth0 is the “Nobody ever got fired for buying IBM” product of Auth solutions. It’s unreasonably expensive, yet is the standard solution for Enterprise companies. Their pricing is not public above 10k MAU, but from what I’ve read they are the only company that raises the per-user cost instead of lowering it as you get bigger. Instead of offering volume discounts, you get volume “punishment”.
They are at least super safe, right? No, they got hacked twice in the last 12 months.
KeyCloak is the open-source alternative, but even seasoned ops professionals are talking about configuring it like wartime stories.
WorkOS - if I understand correctly - is only providing Enterprise features, like SAML. That is, it’d be used in addition to your regular auth solution, thus is not what I was looking for. BTW, they have a really strong frontend team, they are behind Radix UI.
Synchronization
Before I conclude this post, I want to write about the issue with synchronization: Where do you store your user’s login details?
In a classic, server-side framework like Django or Rails, of course, everything is in the production DB. You have foreign keys and everything is guaranteed to be correct and up-to-date.
Now, what happens when you start using a hosted auth solution? Half of your user’s data (email, password, SSO tokens) has to live in their DB, while the other half (the actual user-created data, subscription info, etc.) lives in your DB.
As I’ve written for Firebase, I believe constantly synchronizing these two DBs is not an easy feat. Most hosted auth products have a JSON API and webhooks that allow you to build such a solution, but it’s one of those areas where I can imagine even years into a startup’s life you will encounter bugs and edge cases.
Now, with a self-hosted version, you can actually share the same DB, as all self-hosted solutions support PostgreSQL. That alone is an amazing selling point in my opinion. Thus strangely, a paid option might be more complicated in the long run compared to a self-hosted one.
This is one aspect where Supabase is above all the other hosted solutions: their auth uses the same DB as your application server, hence it’s a hosted auth solution where the DB is shared with your app DB.
Conclusion
I’ve spent way more time finding an auth solution than I originally wanted, but I think I’ve learned a lot in the process. I started with Supabase but even after weeks of trying to make it work, the experience was so bad with random logouts that I just couldn’t consciously choose it.
After reviewing many options, I recommend the following:
If your project can afford the pricing of Clerk, go with them.
If not and you have backend / devops experience, choose Ory. I’ll write a guide for it in the next post.
Otherwise, choose Firebase. Be aware though that the free version will probably be deprecated in the future.
If you are interested in my experience with Ory, feel free to subscribe here, it’ll be my next post.
You can also contact me on X, bsky or in email.
Update 12th Nov 2024: There was a great HN discussion about this article.
Hey ! I recently choose LogTo for authentication, great team and they are shipping fast 👍
https://logto.io/
WorkOS founder here 👋 We now supports all forms of auth via AuthKit 🔥 https://authkit.com
it has everything you need including registration, password auth, social, MFA, and of course SSO+SCIM. The user management system supports flexible org modeling capabilities and upcoming features like RBAC, session management, and advanced security controls.
Latest updates on our changelog: https://workos.com/changelog