Our verdict
-
Buy account UI
Clerk is the candidate when prebuilt UI and organizations save development work.
Check MRU billing and feature gates, including MFA and organization size. Hosted authentication was not tested.
-
Own sessions
Auth.js passed our local session and negative checks.
50 reads had a 3.405 ms median. You still own account lifecycle, abuse controls and deployed runtime compatibility.
-
Plan real email
Supabase Auth needs production delivery configuration.
The default sender is limited to authorized team addresses and two messages/hour. This limit is documented, not tested.
At a glance: identity service or session library?
Clerk and Supabase Auth are hosted identity services. Auth.js is a library you integrate into an application and operate. Comparing only their free user counts hides the main purchase decision: whether you want a vendor to own account lifecycle and auth UI, or your team to implement and support those pieces.
We tested Auth.js core 0.41.3 locally with Web Request/Response APIs, a fixed test Credentials provider and JWT sessions. Fifty authenticated session reads had a 3.405 ms median and 5.761 ms p95. Wrong-password login, a corrupted session cookie and post-logout session reads all returned no session. The fixture establishes basic session behavior, not production identity security.
Hosted authentication was not tested. Clerk signup reached email verification and the new-application URL, but our browser flow ended before an application or user login test was completed. Supabase Auth was researched, not provisioned. We did not create a hosted auth application, send customer confirmation emails, configure OAuth or deploy this fixture to Cloudflare.
Which problem each option actually solves
Clerk is the first candidate when prebuilt sign-in/profile components and organization management would otherwise take development time. Its plan table includes both UI/API functionality and feature restrictions. Supabase Auth is a candidate when you want hosted accounts and JWTs to work alongside services you already use in Supabase; its Auth guide describes password, passwordless and social flows. This comparison is about identity and session integration, not database hosting.
Auth.js is a candidate when you already own the application and want a framework integration around providers and sessions. Its core reference describes the Request/Response entry point; the session guide explains JWT and database strategies. It does not supply a hosted identity dashboard, customer support process or automatic password-account implementation simply by being installed.
| Decision | Auth.js | Clerk | Supabase Auth |
|---|---|---|---|
| Category | Application library | Hosted identity and UI | Hosted identity service |
| Free unit | No hosted user allowance; you run the code | 50,000 monthly retained users/app | 50,000 monthly active users |
| UI ownership | Your app and chosen integration | Prebuilt auth/profile UI included | Build/integrate UI around Auth APIs |
| Team organization model | Implement your own application rules | 100 retained organizations/app; up to 20 members each | Design your app’s tenancy and authorization |
| Infrastructure responsibility | App runtime, session secrets, provider config and any adapters | Vendor service plus your app integration | Vendor service plus your app integration |
| Hands-on coverage here | Local Credentials/JWT fixture | Signup only; no app test | Not tested; documentation only |
Numeric allowances and organization restrictions come from Clerk pricing and Supabase pricing, checked Oct 1, 2026. The categories are our architectural interpretation of the documented products. The table is not a speed ranking.
Our Auth.js fixture and what the checks mean
The local Node 20.19.2 server passes HTTP requests into Auth(). It uses a random process-local secret, JWT session strategy, and one hardcoded test username/password. The provider returns a synthetic user only for the fixture’s valid credential. There is no password database, hashing service, signup, reset flow, email delivery, MFA or external OAuth provider.
const config = {
secret: process.env.AUTH_SECRET,
trustHost: true, // local fixture; evaluate proxy trust in production
basePath: "/auth",
session: { strategy: "jwt" },
providers: [/* configured identity provider */],
};
const response = await Auth(request, config); This sketch deliberately omits our fixed password stub. It is not a password-authentication recipe. Auth.js’ Credentials guide says you must provide your own credential handling. In production, validating passwords, rate limiting attempts, account recovery and safe enrollment remain application responsibilities when choosing that approach.

| Test | Observed result | What was not established |
|---|---|---|
| Anonymous session read | 200 with null session | Protected-resource authorization was not exercised |
| Wrong test password | No authenticated session | No brute-force resistance or password hashing test |
| Valid credential callback | Authenticated synthetic user | Callback response 14.281 ms; not OAuth exchange time |
| 50 authenticated reads | Median 3.405 ms; p95 5.761 ms | Local HTTP only; not provider latency |
| Changed last four cookie characters | Null session | No full cryptographic or penetration audit |
| Logout then session read | Null session | Other-device logout/revocation not tested |
| Session-token value size | 457 bytes | One synthetic user; cookie attributes/name add overhead |
The 50 reads were sequential, using the same valid session, with p95 reported as the 48th sorted sample. The range was 2.434–6.313 ms. We excluded login and negative tests from that latency distribution. Request records retain status, timing and synthetic response bodies; CSRF values are redacted and encrypted session tokens were not committed.
Clerk’s current free unit is MRU, not MAU
Clerk’s February 5, 2026 changelog increased the free allowance to 50,000 monthly retained users. The current pricing page defines retained users around returning at least a day after signup. Supabase meters monthly active users. These are different populations: a signup that never returns can affect one count differently from the other. Do not assume equal headline allowances produce equal usage bills.
Clerk Hobby currently includes unlimited applications, up to three dashboard seats, a fixed seven-day session lifetime and one day of application logs. MFA and custom session lifetime appear in Pro. The free organization allowance is 100 monthly retained organizations per application, with up to 20 members each. A SaaS with small usage but a requirement for MFA or larger organizations may need paid features long before crossing its user allowance. These are documented restrictions; we did not hit them in a dashboard.
The price display we fetched shows Pro at $20/month billed annually, with 50,000 MRU included per app and graduated overages; the first displayed additional-user rate is $0.02 per month. It is not a quote for month-to-month billing or an all-in estimate for B2B add-ons. Pro and every other paid tier are not tested.
Supabase’s email boundary matters before its user ceiling
The Free plan includes 50,000 MAU and two active projects, with inactive free projects subject to pausing after one week. Its total-user count is not the same as MAU. We did not create a project or measure pausing, so these remain documented limits.
For email/password confirmation or magic links, the SMTP guide is the launch-critical source. Supabase’s built-in mail sender accepts only authorized team addresses and is currently limited to two messages per hour. The docs explicitly position it for non-production exploration. A 50,000-MAU free allowance therefore does not mean you can send signup confirmations to 50,000 customers using the default sender.
| Supabase Auth operation | Documented current limit | Scope / caveat |
|---|---|---|
| Built-in email sending | 2 emails/hour per project | Team addresses only without custom SMTP |
| New custom SMTP configuration | Initial 30 emails/hour | Adjustable; sending-provider costs and limits also apply |
| Signup/sign-in family | 30 requests / 5 minutes per IP | Token bucket allows bursts; anonymous sign-in is separate |
| Token endpoint | 150 requests / 5 minutes per IP | Password, refresh, ID-token and PKCE grants |
| OTP repeat request | 60-second window per user | Email-delivery quotas still also apply |
| Anonymous signup | 30 requests/hour per IP by default | Separate configurable bucket |
Rates come from the rate-limit reference and SMTP configuration, checked Oct 1, 2026; none was hit. When your server proxies auth requests, many users can appear to share an IP. The reference describes an explicitly enabled forwarding mechanism that requires a secret key. Do not copy that secret into browser code while trying to fix a rate-limit problem.
Supabase Pro starts at $25/month and includes 100,000 MAU before auth overages in the public pricing display. That subscription bundles more than identity; extra projects and services can affect the total. We did not test the plan or compare its whole platform to Clerk’s identity-only budget. Custom SMTP remains a separate delivery decision on either free or paid hosting.
Cloudflare compatibility: verify the entire dependency chain
The Auth.js edge guide says core functionality is designed around portable APIs but warns that database adapters and other dependencies can break in an edge environment. Our adapter-free JWT fixture uses Web Request/Response APIs; running that fixture on Node does not prove a Cloudflare Worker bundle will work. Provider callbacks, crypto dependencies, framework adapters and proxy headers still need a deployed test.
For Clerk, use the current backend SDK and the integration documented for your actual framework/runtime; the backend reference covers createClerkClient. We did not validate a Hono middleware package, verify hosted Clerk JWTs at the edge or measure JWKS fetching. Supabase’s HTTP-based auth APIs are usable from a fetch-based application, but cookies, refresh behavior and allowed redirect URLs are still your integration work. These are architectural options, not completed Cloudflare integrations.
With a static front end on Pages and an API on Workers, decide where sessions are checked and where cookies are set before choosing the vendor. Keep privileged keys in server bindings, validate authorization in the API and configure exact OAuth redirect URLs for production and previews. A user being signed in does not by itself grant access to another customer’s data. We did not test tenancy isolation.
JWT convenience, revocation and operational ownership
Our test used a JWT session because it avoids a session database in the fixture. The session-strategy guide explains the tradeoff: deleting the browser cookie signs that browser out, while early revocation of an already-issued token needs additional design. A fast local read is not a complete account-deletion or other-device logout strategy. Our logout check only established that the fixture client stopped presenting an authenticated session afterward.
If you choose Auth.js, budget work for secret persistence/rotation, provider setup, account linking, reset and verification flows, abuse controls, cookie policy and observability. Our random per-process test secret was intentionally disposable; a real deployment needs consistent secrets across instances and restarts. If you choose a hosted service, budget for SDK upgrades, exports/migration, provider configuration and handling service outages. Neither choice removes application authorization work.
Real user reports and maintenance signals
Auth.js issue #11999 reports a SvelteKit application working locally but failing on Cloudflare Workers. Issue #11184 reports a crypto-export error in a Cloudflare build. Both are older framework-specific reports; neither is proof that our current core version fails. They support our decision to keep deployed runtime compatibility explicitly untested rather than extrapolate from Node.
The Auth.js release page shows security maintenance in the v4 line; that is a different package line from our core fixture. The current docs also state the project is part of Better Auth. This review does not treat Better Auth as tested or interchangeable with Auth.js. We checked Supabase’s changelog and Clerk’s pricing changelog alongside current docs so historical plan numbers do not silently enter the comparison.
Our decision for an indie SaaS
Start with Clerk when the UI and user/organization management save work that you would otherwise build, and verify the feature gates you need. Start with Supabase Auth when it fits an existing Supabase application, but arrange real email delivery before opening registration. Start with Auth.js when you want to own the implementation and can support its account lifecycle and runtime integration. Our measured session checks make the library a credible local prototype option; they do not rank hosted reliability or security.
Before shipping any of the three, test real signup, duplicate-email handling, password reset or provider recovery, email delivery, OAuth state handling, logout across devices and a cross-tenant authorization attempt in your deployment. Those are the missing tests that would most change this recommendation. No commission-linked vendor URL is used on this page.
Who should not use this
Do not choose Auth.js as a shortcut around password storage, account recovery or abuse prevention. Do not launch public email signup on Supabase’s default sender. Do not assume Clerk Hobby includes every paid security or organization feature. Hosted performance and security are not ranked by our local tests.
On this page · 10 sections
- Our verdict
- At a glance: identity service or session library?
- Which problem each option actually solves
- Our Auth.js fixture and what the checks mean
- Clerk’s current free unit is MRU, not MAU
- Supabase’s email boundary matters before its user ceiling
- Cloudflare compatibility: verify the entire dependency chain
- JWT convenience, revocation and operational ownership
- Real user reports and maintenance signals
- Our decision for an indie SaaS