{
"$type": "site.standard.document",
"coverImage": {
"$type": "blob",
"ref": {
"$link": "bafkreibqrj7okq7l4eyw3fq4u33dvcwqvd2kecxlyuglg7custcer3ft24"
},
"mimeType": "image/jpeg",
"size": 58024
},
"description": "The perils of logging in",
"path": "/better-auth",
"publishedAt": "2026-05-06T00:00:00.000Z",
"site": "at://did:plc:a2rdzfdxkjwerrfrpbwcipb2/site.standard.publication/3jd443afc2222",
"textContent": "In 2023, I wrote about\nhow Val Town moved away from Supabase\nand toward a more conventional database setup. We were using a lot of Supabase's\nfunctionality, including their authentication. So when it came time to move we\nfound equivalents: Render for the database, and\nClerk for authentication. But life came at us fast, and by\nlate 2023 we had an issue filed: get off of Clerk. That issue was finally closed\na month ago, when we switched to Better Auth.\n\nSome important context is that Clerk is a major success. They just raised\n50 million dollars and they have lots of\nsatisfied users. Heck, in related news Supabase\nraised 100 million dollars at a 5 billion dollar valuation.\nCongratulations to both of them. I would make a terrible venture capitalist.\nWhatever opinions and experiences I hold about authentication and row level\nsecurity are secondary to these numbers and proof. You can't argue with success.\n\nBut still, I am happy to have closed that issue and switched to Better Auth.\nIt's been a tough experience, with a lot of workarounds, bugs, and outages. The\narchitecture of Val Town sharply conflicted with Clerk's expectations.\n\nThe core issue\n\nThe core issue is that Clerk tried to be your users table and your sessions\ntable. I think they've been shifting away from saying this, but it started from\na pretty extreme place: there's a 2021 blog post titled\n\"Consider dropping your users table\".\nThere's a YouTube video from 2023 called\nDELETE your Users table. I\nstrongly suggest you don't!\n\nThere are two big problems with farming out your users table to a third-party\nservice.\n\nClerk was a pretty bad replacement for a users table because it was heavily\nrate-limited and not very reliable. When we initially switched, I assumed we\ncould load user data from Clerk's API whenever we needed to. After all, we want\nto know things like user settings, avatar URLs, and emails. Clerk's SDK made\nthis pretty convenient: the rootAuthLoader, the thing that handles auth for your\nwhole application, had a nice little option called which would do the\nrequest for you. Worked great in development. In production, the rate limit for\nthat endpoint was five requests per second. For the whole account, across all\nusers. Tough! A pretty bad footgun, that we discovered in production, and was\neventually\nfixed by removing the option.\n\nThe rate limiting hit us especially hard for the social aspect of Val Town. For\nexample, let's say you have a social website, so a lot of pages have lists of\ncontent from other users, and usernames and avatars to identify them. Clerk's\ndefault UIs are based on the assumption that a user only sees their own avatar\nand needs their own settings and information, and they can get all of that stuff\nfrom their nifty JWT token.\nSocial websites like Val Town completely break this assumption, and the advice\nwas to sync avatars and other information between Clerk and our users table: so\nnow we have two authorities for that information, and the complexity of two\nusers tables.\n\nSo we had to sync Clerk's data to our database by using webhooks, which meant\nthat signing up was convoluted and tricky - for a few moments, a user has a\nClerk account but no Val Town database row. Or, because our platform requires\nusernames, users can be in a state where they have a Clerk account, a database\nrow, but their account is incomplete. Our user settings had to be split between\nthings that Clerk controlled, like auth strategies, and things that we needed,\nlike usernames and editor settings.\n\nThe second is that Clerk became a single point of failure for all our user\nsessions. Cookie-based user sessions are usually short-lived with constant\nrefreshing: that way they can be invalidated quickly. But that also means that\nevery few minutes users need to swap their session cookies for new ones. So when\nsomeone's login session needed to be refreshed, it was a subdomain of Val Town\nthat passed the request to Clerk that did the refreshing. We didn't have a\nsessions table or any responsibility over sessions.\n\nThat's great, if you're trying to keep avoid any responsibility for\nauthentication, but on the other hand, if Clerk goes down, the whole website\ngoes down. Clerk outages don't just break the login & logout flow, they make the\nsite unusable to people who are already logged in. And Clerk went down pretty\noften, and went down for long periods of time.\nSince May 2025 it's been teetering between two and\nthree nines of uptime. There isn't data from before then, but I remember many\ntimes that we had a broken site and no way to fix it because of this single\npoint of failure.\n\nA hard lesson you learn building a complex system is that its reliability is the\nminimum of the combined reliability of its critical parts.\n\nBesides these two major issues, there were\nother bugs and problems we encountered.\nMost got fixed eventually, but I spent a lot of time battling the \"Stale Issue\nBot\" from auto-closing them.\n\nThree-ish years\n\nIf it was so bad, why didn't we switch away immediately?\n\nFirst of all, even though this will be the second \"switching from X to Y\"\narticle I've written, and I'm not trying to make it a habit. Making decisions\nand sticking with them is good for development velocity and team sanity. We're\nnot trying to rewrite Val Town any more than is absolutely necessary. And\nwriting critique is less fun and positive than building.\n\nAnd Clerk did some things well. They provided SDKs for all the tech we were\nusing: Remix, Fastify, and Express. They did a decent job of keeping up with the\nchurn of those frameworks, a task that I know is a full-time job. And their\nadministration and anti-abuse measures were decent at helping us solve customer\nsupport issues and keep scammers at bay.\n\nWhere Clerk definitely shines is relatively simple, heavily frontend apps that\ndon't have a social component, so they don't need a users table. It was\nincredibly easy to get started, affordable, and the Clerk dashboard is pretty\nnice.\n\nAnd there aren't a ton of great options for authentication. The bar for a Clerk\nreplacement was actually pretty high: a lot of open source auth solutions are\nancient and semi-abandoned. Authentication-as-a-service platforms had vendor\nrisk and potentially the same problems as those in Clerk. The right level of\ntechnical control is hard to nail. We don't want to build authentication from\nscratch and open Val Town up to new and embarrassing new vulnerabilities, but we\nalso don't want to offload so much responsibility to a provider. Definitely not\ntrusting third-party session management again.\n\nBetter Auth enters the frame\n\nBetter Auth checked a lot of boxes right out of the\ngate: high code quality, good integrations with different frameworks, and truly\nusable as an independent open source project.\n\nThere still is vendor risk: it's a big, complex codebase developed mostly by one\ncompany. There's always vendor risk. But we are no longer dependent on a third\nparty staying online in order for sessions & user auth to work.\n\nA close second place was AuthKit from\nWorkOS. I trust WorkOS and AuthKit is incredibly slick,\nbut after bouncing between two vendors, it was important to me to find something\nthat could work independently and was open source at the core.\n\nI find Better Auth's dashboard and paid add-ons to be really clever, too. We\nmanage all of our data, and a plugin provides an API on our site that lets their\ndashboard pull information and do some light user administration. Better Auth's\npaid service (called 'Infrastructure') is basically stateless in the way that we\nuse it, and uninvolved in session management.\n\nIn short, so far it really has been better.\n\nAnd reluctantly I have to hand it to the LLMs here: with the augmentation of the\nrobots, we were able to take the more complex route of supporting both Better\nAuth and Clerk for a transitional period of two weeks. Every endpoint that\nhandled authentication would accept either kind of cookie, and users slowly\nmoved over to Better Auth because that was the kind of session that the sign-in\npage provided. Like anything related to security, close reading, rewriting, and\ntesting of all of the code was necessary to make sure we didn't self-own, and\nthe eventual pure-Better Auth auth was handwritten entirely.\n\nBetter Auth also works pretty well with Vals: you can\ntry out the Better Auth starter template\nto add authentication to your code on Val Town.\n\n---\n\nI've learned a lot along the way. You really do depend on upstream providers for\nyour uptime, and should think hard about how exposed you are to that risk.\nProducts can be good for a lot of use-cases and really successful and still not\nthe right thing for your specific problem. The world of software changes quickly\nand the right solution might not exist at the moment you need it, but might\nappear a year later.",
"title": "From Supabase to Clerk to Better Auth"
}