Authentication (Tools)
The auth-as-a-service landscape. Clerk, Better Auth, Auth.js, Auth0, Supabase Auth, WorkOS — and when to pick each.
Authentication (Tools)
In one line: Don't build your own auth. Use Clerk for a polished commercial experience; Better Auth or Auth.js if you want open-source self-hosting; Auth0/WorkOS for enterprise needs.
Authentication is hard. Modern best practice is to outsource it — pick a service, drop in their components, focus on your actual product. This page is the landscape of those services.
If you want the conceptual background (sessions vs JWTs, passwords vs passkeys, RBAC vs ABAC), see the Authentication and Authorization pages in chapter 1.
Clerk
Drop-in auth as a service. Pre-built React components.
import { SignIn, SignUp, UserButton } from '@clerk/nextjs';
export default function Page() {
return <SignIn />; // Full sign-in UI in one component
}
Strengths: Best UX, fast to integrate, lots of features (multi-factor, social, magic links, passkeys). Trade-offs: Expensive at scale (~$25/month base, scales by MAU).
Better Auth
Open-source, TypeScript-native, self-hostable. Rising fast in 2026.
import { betterAuth } from 'better-auth';
export const auth = betterAuth({
database: { provider: 'postgres', ... },
emailAndPassword: { enabled: true },
socialProviders: { google: { ... } },
});
Strengths: Free, full control, modern API. Trade-offs: You self-host (more responsibility); newer (less battle-tested than Clerk/Auth0).
Auth.js (formerly NextAuth)
Open-source, framework-integrated. The historical default for Next.js.
Strengths: Free, mature, deep Next.js integration, supports dozens of OAuth providers out of the box. Trade-offs: Less polished DX than Clerk; more boilerplate for custom flows.
Auth0
Mature, enterprise-friendly. Acquired by Okta.
When to use: Enterprise customers, complex compliance requirements, established product.
Supabase Auth
Bundled with Supabase. Convenient if you're already using Supabase for DB.
import { createClient } from '@supabase/supabase-js';
const supabase = createClient(URL, KEY);
await supabase.auth.signInWithPassword({
email: 'tony@x.com',
password: 'secret',
});
WorkOS
Enterprise SSO/SAML/SCIM as a service. The go-to when selling B2B and customers demand enterprise auth.
Decision matrix
| Need | Recommendation |
|---|---|
| Fast time-to-market, paid OK | Clerk |
| Open-source, self-host | Better Auth |
| Existing Supabase user | Supabase Auth |
| Enterprise B2B | Clerk + WorkOS, or Auth0 |
| Maximum customization | Build on Better Auth or Auth.js |
For solo / startup projects, you almost certainly fit in someone's free tier:
- Clerk — 10,000 monthly active users free.
- Supabase Auth — 50,000 monthly active users free.
- Better Auth — fully free and self-hostable.
- Auth.js — fully free.
You should not be building your own auth from scratch to "save money." The free tiers are extremely generous and the engineering cost is enormous.
Common mistakes
- Rolling your own auth "to save money." The free tiers cover 10k–50k MAU. The engineering hours you save are worth more than the subscription, and you avoid the entire class of session-fixation, timing-attack, and password-hashing bugs.
- Storing the auth provider's JWT in
localStorage. Vulnerable to any XSS bug on your domain. Use httpOnly, Secure, SameSite=Lax cookies (which all these providers default to). If a tutorial tells you to copy a token intolocalStorage, it's outdated. - Treating Clerk as both your auth and your user database. Clerk is the source of truth for identity; your
userstable is the source of truth for application data. Mirror the Clerk user ID into your DB on first sign-in and join from there — don't try to store orders, preferences, or relationships in Clerk's user metadata. - Locking in too early without an export plan. Auth providers are stickier than they look — sessions, password hashes, OAuth connections all live there. Before committing, confirm there's an export API (Clerk, Auth0, Better Auth all have one). If a vendor won't let you leave with your users, that's a flag.
- Confusing authentication with authorization. The auth provider tells you who the user is. What they're allowed to do (roles, permissions, row-level access) is your app's job. Don't expect Clerk or Auth0 to enforce "this user can edit this document" — you check that on the server, every request.
Page checkpoint
Did auth tools stick?
RequiredWhat's next
→ Continue to Background Jobs — long-running and scheduled work that shouldn't block HTTP requests.