TL;DR
- Clerk wins on developer experience and features (magic links, passkeys, organizations, MFA, embeddable UI components)
- Auth.js (NextAuth v5) wins on cost, control, and portability — fully open source, no vendor lock-in
- Supabase Auth wins when you're already on Supabase — zero extra services, RLS policies integrate directly
Decision rule: Under 10k MAU and want to ship fast → Clerk (free tier). At scale or need self-hosting → Auth.js. Already on Supabase → Supabase Auth.
Comparison Table
| Feature | Auth.js (NextAuth) | Clerk | Supabase Auth |
|---|---|---|---|
| Price | Free | Free (up to 10k MAU) → $25+/mo | Free (up to 50k MAU) → $25+/mo |
| Self-hosted | ✅ | ❌ | ✅ |
| Email/Password | ✅ | ✅ | ✅ |
| Magic Links | ✅ | ✅ | ✅ |
| OAuth Providers | 40+ | 20+ | 20+ |
| Passkeys | ⚠️ Limited | ✅ | ✅ |
| MFA/2FA | Manual | ✅ Built-in | ✅ Built-in |
| Organizations/Teams | Manual | ✅ Built-in | Manual |
| User Management UI | ❌ | ✅ Dashboard + embeddable | ✅ Dashboard |
| Session management | JWT / DB sessions | JWT (opaque) | JWT |
| Bundle size | ~30KB | ~100KB+ | ~40KB |
| Edge Runtime | ✅ | ✅ | ✅ |
| Vendor lock-in | None | High | Medium |
Auth.js (NextAuth v5) — Best Open Source
Price: Free | License: MIT | GitHub Stars: 25k+
The standard open-source authentication library for Next.js. Auth.js v5 (NextAuth) is fully compatible with Next.js App Router and Edge Runtime. You own the auth code — no vendor, no pricing tiers, no lock-in.
// auth.ts — complete Auth.js v5 setup
import NextAuth from 'next-auth';
import Google from 'next-auth/providers/google';
import GitHub from 'next-auth/providers/github';
import Resend from 'next-auth/providers/resend';
import { PrismaAdapter } from '@auth/prisma-adapter';
import { db } from '@/lib/db';
export const { handlers, signIn, signOut, auth } = NextAuth({
adapter: PrismaAdapter(db),
providers: [
Google,
GitHub,
Resend({ from: 'noreply@yoursaas.com' }), // Magic links
],
session: { strategy: 'database' }, // DB sessions for better security
callbacks: {
session: ({ session, user }) => ({
...session,
user: {
...session.user,
id: user.id,
role: user.role, // Custom fields from your DB
},
}),
},
pages: {
signIn: '/auth/signin',
error: '/auth/error',
},
});
// app/api/auth/[...nextauth]/route.ts
export { handlers as GET, handlers as POST } from '@/auth';
Usage in App Router:
// Server component — get session
import { auth } from '@/auth';
export default async function DashboardPage() {
const session = await auth();
if (!session?.user) redirect('/auth/signin');
return <Dashboard user={session.user} />;
}
// Middleware — protect routes
import { auth } from '@/auth';
export default auth((req) => {
if (!req.auth && req.nextUrl.pathname.startsWith('/dashboard')) {
return Response.redirect(new URL('/auth/signin', req.url));
}
});
What you build yourself with Auth.js:
- Organization/team model (2-4 weeks for B2B SaaS)
- User admin panel
- MFA (can use TOTP libraries)
- Custom user profile pages
Best for: Apps that need full control, budget sensitivity at scale, or self-hosting requirements.
Clerk — Best Developer Experience
Price: Free (up to 10k MAU) → $25+/month | GitHub Stars: 3k+ (SDK)
Clerk is the fastest auth setup for Next.js in 2026. Pre-built UI components (SignIn, UserButton, OrganizationSwitcher), middleware in one line, and embeddable user management components you don't have to build.
// middleware.ts — protect routes in one call
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';
const isProtectedRoute = createRouteMatcher([
'/dashboard(.*)',
'/api/(.*)',
'/settings(.*)',
]);
export default clerkMiddleware((auth, req) => {
if (isProtectedRoute(req)) auth().protect();
});
export const config = {
matcher: ['/((?!.+\\.[\\w]+$|_next).*)', '/', '/(api|trpc)(.*)'],
};
// app/dashboard/page.tsx — get current user
import { currentUser, auth } from '@clerk/nextjs/server';
export default async function Dashboard() {
const user = await currentUser();
const { orgId, orgRole } = await auth();
return (
<div>
<h1>Welcome, {user?.firstName}</h1>
{orgId && <p>Organization: {orgId} ({orgRole})</p>}
</div>
);
}
Embeddable UI components:
// app/(auth)/sign-in/[[...sign-in]]/page.tsx
import { SignIn } from '@clerk/nextjs';
export default function SignInPage() {
return (
<div className="flex justify-center">
<SignIn /> {/* Complete sign-in UI, no code needed */}
</div>
);
}
Clerk Organizations (built-in teams):
// Create organization
const org = await clerkClient.organizations.createOrganization({
name: "Acme Corp",
createdBy: userId,
});
// Invite member
await clerkClient.organizations.createOrganizationInvitation({
organizationId: org.id,
emailAddress: "newmember@acmecorp.com",
role: "org:member",
redirectUrl: "https://yoursaas.com/onboarding",
});
Clerk pricing at scale:
| MAU | Monthly Cost |
|---|---|
| 0-10k | Free |
| 10k-50k | ~$25-$250 |
| 100k | ~$500 |
| 500k | ~$2,000+ |
At 100k+ MAU, Auth.js + self-hosted infrastructure is significantly cheaper.
Best for: Speed-to-launch, teams that don't want to maintain auth code, products needing organizations/teams out of the box.
Supabase Auth — Best When Already on Supabase
Price: Free (up to 50k MAU) → $25+/month | GitHub Stars: 75k+ (Supabase)
When your entire backend is Supabase — PostgreSQL database, real-time subscriptions, storage — using Supabase Auth is the natural choice. Auth integrates with Row Level Security (RLS) policies: database queries are automatically scoped to the authenticated user.
// Authentication
const { error } = await supabase.auth.signInWithPassword({
email,
password,
});
// Magic link
await supabase.auth.signInWithOtp({ email });
// OAuth
await supabase.auth.signInWithOAuth({
provider: 'github',
options: { redirectTo: 'https://yoursaas.com/auth/callback' },
});
// Sign out
await supabase.auth.signOut();
Row Level Security with auth.uid():
-- Only users can access their own data
CREATE POLICY "Users can view own profile"
ON profiles
FOR SELECT
USING (auth.uid() = user_id);
-- Automatic data isolation — no explicit WHERE clause needed
-- SELECT * FROM profiles returns only the current user's profile
// No manual user filtering needed in application code
const { data: profile } = await supabase
.from('profiles')
.select('*')
.single();
// Returns only the current user's profile — RLS enforces this
Supabase Auth features:
- Email + password with email confirmation
- Magic links (passwordless)
- OAuth: Google, GitHub, Twitter, Discord, LinkedIn, and more
- Phone auth (OTP via SMS)
- MFA (TOTP)
- JWT customization with hooks
- Server-Side Rendering compatible
Best for: Apps where the entire backend is Supabase — auth integrates with zero additional configuration.
Which Boilerplates Use Which
| Boilerplate | Auth Solution | Why |
|---|---|---|
| ShipFast | Auth.js (NextAuth) | Cost control, portability |
| Supastarter (Next.js) | Supabase Auth | Full Supabase stack |
| Makerkit | Supabase Auth or Clerk | Configurable |
| T3 Stack | Auth.js | OSS alignment |
| Epic Stack | Custom (Remix session) | Full control |
| Open SaaS | Wasp Auth (Auth.js) | OSS alignment |
| create-t3-app | Auth.js | OSS alignment |
The Organization/Team Decision
If your SaaS needs team accounts (B2B SaaS), this changes the auth choice significantly:
- Clerk — Organizations built-in, invitations, roles, and org-level metadata. Free under 10k MAU. Saves 2-4 weeks of development.
- Auth.js — Add organization model yourself (~2-4 weeks). Full control, no added cost at scale.
- Supabase — Add organization model yourself (~2-4 weeks). Can leverage RLS for automatic org scoping.
For early-stage B2B SaaS: Clerk's organization feature saves weeks. At scale (>100k MAU), Auth.js's lower marginal cost becomes significant.
Migration Considerations
Switching auth providers after launch is painful — session tokens change, users need to re-authenticate, and all auth-related code needs updating. Make the right choice upfront:
| If you're... | Use |
|---|---|
| Building MVP, want to ship fast | Clerk |
| Cost-sensitive or need self-hosting | Auth.js |
| All-in on Supabase | Supabase Auth |
| Building B2B with teams, early stage | Clerk |
| Building B2B with teams, post-product-market-fit | Auth.js + custom org model |
| Enterprise (SSO/SAML required) | WorkOS or Clerk Enterprise |
Setting Up Each Provider
Auth.js Quick Start
npm install next-auth@beta @auth/prisma-adapter
// auth.ts
import NextAuth from 'next-auth';
import GitHub from 'next-auth/providers/github';
import { PrismaAdapter } from '@auth/prisma-adapter';
import { db } from '@/lib/db';
export const { handlers, signIn, signOut, auth } = NextAuth({
adapter: PrismaAdapter(db),
providers: [GitHub],
});
// app/api/auth/[...nextauth]/route.ts
export { GET, POST } from '@/auth';
Auth.js requires a database for session persistence (unless using JWT sessions). Prisma + PostgreSQL is the most common setup. The PrismaAdapter handles creating the required User, Account, Session, and VerificationToken tables via Prisma migrations.
Clerk Quick Start
npm install @clerk/nextjs
// middleware.ts — that's it for basic protection
import { clerkMiddleware } from '@clerk/nextjs/server';
export default clerkMiddleware();
export const config = { matcher: ['/((?!.*\\..*|_next).*)', '/'] };
Clerk requires no database setup — it manages all user data. The CLERK_SECRET_KEY and NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY environment variables are the only configuration needed for basic auth.
Supabase Auth Quick Start
npm install @supabase/supabase-js @supabase/auth-helpers-nextjs
// lib/supabase.ts — server client with cookie management
import { createServerComponentClient } from '@supabase/auth-helpers-nextjs';
import { cookies } from 'next/headers';
export const supabase = () => createServerComponentClient({ cookies });
Supabase Auth is configured through the Supabase dashboard — no additional library configuration needed. Email templates, OAuth providers, and JWT settings are all dashboard-managed.
Pricing at Scale
Real cost comparison at different MAU levels:
| MAU | Auth.js | Clerk | Supabase Auth |
|---|---|---|---|
| 1,000 | $0 | $0 | $0 |
| 10,000 | $0 | $0 | $0 |
| 25,000 | $0 | $37.50/mo | $0 |
| 50,000 | $0 | $75/mo | $0 |
| 100,000 | $0 | $150/mo | $25/mo |
| 500,000 | $0 | $750/mo | $125/mo |
Auth.js stays free at any scale. At 100k MAU, you'd pay $1,800/year for Clerk or $300/year for Supabase Auth. For a SaaS business generating revenue, these costs are modest — but they're real and ongoing.
The more important cost for Clerk is the initial time savings: organizations, MFA, passkeys, and embeddable UI components save 4-8 weeks of development. At a developer's hourly rate, Clerk pays for years of subscriptions in the first month.
Key Takeaways
- Auth choice is long-lasting — switching providers after launch requires user re-authentication
- Clerk is best for speed-to-launch, B2B SaaS teams, and teams that don't want to maintain auth code
- Auth.js is best for cost control at scale, full data ownership, and maximum flexibility
- Supabase Auth wins when Supabase is your database — the RLS integration eliminates a class of access control bugs
- Organization/team management takes 2-4 weeks with Auth.js or Supabase, 2 days with Clerk
- All three providers support the same OAuth providers (Google, GitHub) and magic links
Multi-Factor Authentication Across All Three Providers
Multi-factor authentication (MFA) has moved from a nice-to-have to a baseline expectation for B2B SaaS. Enterprise security teams increasingly require MFA as a condition for approval. Understanding how each provider handles MFA before committing saves a painful architecture change after enterprise customers start asking for it.
Clerk has the best MFA experience of the three. TOTP (authenticator app), SMS one-time passwords, and backup codes are built into Clerk's pre-built UI components and activated with a single flag in the dashboard. Users manage their MFA preferences in the Clerk account portal — you do not need to build any MFA UI. For B2B SaaS targeting enterprise customers, Clerk's out-of-the-box MFA is the fastest path to "yes" when procurement asks about two-factor authentication support.
Auth.js supports TOTP MFA through the community auth-otp extension or custom middleware, but it requires building the MFA enrollment and verification UI yourself. The otpauth npm package handles TOTP generation and validation server-side. Budget 3-5 days to build a complete MFA flow: enrollment screen, QR code generation, verification on login, backup codes, and the database schema to store MFA secrets securely.
Supabase Auth includes TOTP MFA as a built-in feature enabled in the Supabase dashboard. The supabase.auth.mfa.enroll() and supabase.auth.mfa.challenge() API calls handle enrollment and verification. Supabase's MFA is less polished than Clerk's but more complete than Auth.js's community options. For teams already using Supabase, this is the practical MFA path — it works with no additional dependencies.
Migration Risk: What Switching Auth Providers Actually Costs
Auth is one of the most expensive decisions to reverse after launch. Understanding the migration cost is essential before committing to a provider — "we can switch later" is rarely true in practice.
Migrating from Clerk to Auth.js (or vice versa) requires re-authenticating every existing user. Passwords do not transfer — Clerk hashes with its own system, Auth.js uses bcrypt. OAuth accounts need to be reconnected. Sessions are invalidated. The migration window means some users will be logged out mid-session. For a product with active users, a forced logout of your entire user base is a customer experience event significant enough to drive churn. Small startups have lost users this way.
The safer migration path — if you must switch — is running both systems in parallel during a transition period. New signups use the new provider; existing users are prompted to re-link their account the next time they log in. This adds complexity but avoids a forced logout. The parallel-provider approach requires careful session handling and typically takes 4-6 weeks to complete cleanly for an active product.
The practical implication: choose your auth provider with the assumption that you will live with it for at least two years. If you are a solo founder validating an idea, Clerk's fast setup is worth the cost risk. If you expect significant scale, Auth.js or Supabase Auth's pricing stability is worth the extra setup time upfront. The time to evaluate this carefully is before you have 500 users, not after. For a detailed breakdown of newer auth options that have emerged alongside these three, see the better-auth vs Clerk vs NextAuth comparison.
Which Boilerplates Use Which Auth Provider
The auth provider bundled with your boilerplate determines a significant amount of your early architecture. Most popular Next.js boilerplates fall into clear camps:
Clerk-first boilerplates include Shipfast and several Supabase-adjacent starters. The reasoning is consistent: Clerk's pre-built components make the initial setup fast, and its organizations feature makes B2B auth nearly zero-code. For solo founders who want to move quickly, this is the dominant choice.
Auth.js boilerplates include the T3 Stack and most open-source community starters. Auth.js is the natural fit for T3 because Prisma is already handling the database — the PrismaAdapter connects auth to your existing database without a new service dependency. If you want full data ownership and a zero-cost auth stack, T3 with Auth.js is the reference implementation.
Supabase Auth boilerplates are typically Supabase-native starters that already use Supabase for the database. The integration is tight: Row Level Security policies reference auth.uid() directly, which means your authorization logic lives in the database rather than application code. This eliminates a category of access control bugs where the application forgets to check permissions before returning data.
For teams who have not yet committed to a boilerplate and want the most flexibility around auth, starting with best SaaS boilerplates and filtering by auth provider shows you the full landscape of what each approach looks like in production.
Authentication is the most security-critical component of any SaaS boilerplate. A library choice that seems equivalent on features may differ significantly on security defaults — token rotation, session invalidation, rate limiting on auth endpoints, and CSRF protection are areas where implementation quality matters more than API ergonomics.
Find boilerplates using each auth provider in the StarterPick directory.
See our best Next.js boilerplates guide — most include a pre-configured auth solution.
Compare better-auth vs Clerk vs NextAuth for the extended auth comparison including newer options.