Which auth stack should you use for your vibe-coded app?
Clerk for B2B SaaS. Supabase Auth for data-heavy apps. NextAuth for internal tools. Firebase for consumer mobile.
Below: decision tree, hidden pricing traps, cost calculator, and the full setup for using Clerk WITH Supabase.
You've tried 4 different auth providers. Burned two days evaluating. Still not sure. This guide cuts through the noise — pick your project type, get your answer, copy the setup, and ship.
Covers: Clerk vs Supabase Auth vs NextAuth vs Firebase Auth — with a decision tree, comparison table, pricing traps, and the 5 mistakes that break vibe-coded apps.
1. Decision Tree — Pick Your Project Type
Stop evaluating. Match your project to the pattern below and use that auth stack. This covers 95% of vibe-coded apps.
Project Type
SaaS / B2B App
Recommended
Clerk
Orgs, teams, roles, and SSO out of the box. You want users managed for you — not a custom auth system. Clerk is the fastest path from zero to production-grade B2B auth.
Project Type
Marketplace / Community Platform
Recommended
Supabase Auth
You need auth + database + RLS working together. Supabase Auth ties into Row Level Security natively — every user action is scoped to their data. This is the best combo for marketplaces.
Project Type
Consumer Mobile / Web App
Recommended
Firebase Auth
Google Sign-In, Apple Sign-In, phone OTP, anonymous auth — all in one SDK. If you need social logins and mobile-first flows, Firebase Auth has the best out-of-the-box coverage.
Project Type
Internal Tool / Admin Dashboard
Recommended
NextAuth (Auth.js)
Internal tools don’t need complex user management — they need a quick login gate. Auth.js with a GitHub or Google provider is 30 minutes to working auth with zero ongoing cost. For a brand-new project also look at Better Auth, which the Auth.js project has now joined.
2. Clerk vs Supabase Auth vs NextAuth vs Firebase Auth
Full comparison across the dimensions that actually matter for vibe-coded apps.
| Feature | Clerk | Supabase | NextAuth | Firebase |
|---|---|---|---|---|
| Free Tier | 50k users/app | 50k MAU | Free (OSS) | Free allowance |
| Pricing After | Pro + $0.02/user over 50k | Pro from $25/mo (100k MAU) | Your hosting only | Per MAU + per SMS |
| Setup Time | ~15 min | ~30 min | ~45 min | ~20 min |
| Social Login | ✅ Built-in UI | ✅ Built-in | ✅ via providers | ✅ Best-in-class |
| Email/Password | ✅ w/ UI | ✅ | ✅ Credentials | ✅ |
| Magic Link | ✅ | ✅ | ✅ Email provider | ✅ |
| Phone/SMS OTP | ✅ | ✅ | ❌ (workaround) | ✅ Best |
| MFA / 2FA | ✅ On Pro | ✅ TOTP + phone | ❌ DIY | ✅ |
| Orgs / Teams | ✅ Native | ❌ DIY w/ RLS | ❌ DIY | ❌ DIY |
| Pre-built UI | ✅ Drop-in | ⚠️ Basic | ❌ You build it | ⚠️ FirebaseUI |
| RLS Integration | ⚠️ Manual JWT | ✅ Native | ❌ Manual | ❌ Manual |
| Next.js Support | ✅ First-class | ✅ First-class | ✅ Made for it | ✅ Good |
| User Dashboard | ✅ Full UI | ✅ In Studio | ❌ | ✅ Console |
| Best For | SaaS, B2B | Marketplace, data-heavy | Internal tools, simple apps | Consumer mobile/web |
3. Hidden Pricing Traps Nobody Tells You
Every auth provider looks affordable on the pricing page. Here's what actually shows up on your bill.
Clerk
Pricing TrapFree to 50,000 users, then every extra user is billed
Clerk’s free plan now covers 50,000 monthly retained users per app, and multi-factor authentication is included in Pro rather than sold as a separate add-on. The costs to watch are different: each user beyond 50,000 is billed (listed at $0.02 per month before volume discounts), and the Enhanced B2B add-on — needed for organizations with more than 20 members, custom roles, or verified domains — is $100/mo.
The Numbers
Free: 50k users/app. Pro: MFA included, $0.02 per extra user. Enhanced B2B add-on: $100/mo. Checked October 2026.
Supabase
Pricing TrapFree projects pause after a week of inactivity
The free plan allows 2 active projects and pauses any project that goes a week without activity, which catches out side projects and staging environments. Pro starts at $25/mo with the first project included; each additional project adds compute from about $10/mo. So a main app plus staging plus an admin project is roughly $45/mo, not $75.
The Numbers
Free: 50k MAU, 2 projects, pauses when idle. Pro: from $25/mo incl. 100k MAU, extra projects from $10/mo. Team: from $599/mo. Checked October 2026.
NextAuth
Pricing Trap“Free” means you build the features yourself
Auth.js (NextAuth) is open-source and runs inside your existing Next.js app, so there is no licence fee and no separate auth server to pay for. The cost is engineering time: you need a database for users and sessions, and you build every feature a hosted provider includes — org management, sign-in UI, MFA, a user dashboard. Also note the Auth.js project is now part of Better Auth, which is where new development is happening.
The Numbers
Licence: $0. Runs in your app. Database: whatever you already use. Eng time for orgs/MFA/UI: 40–100+ hours.
Firebase
Pricing TrapPhone sign-in is billed per SMS, not per user
Email/password and social sign-in have a free monthly allowance. Phone sign-in is different: every SMS sent is billed, at a rate that varies by country, and it needs the paid Blaze plan. A consumer app that verifies phones on every signup and re-login can run up a real bill. Check Firebase’s current rate table before choosing phone auth as your main sign-in.
The Numbers
Email and social: free allowance. Phone: billed per SMS by country, Blaze plan required. See firebase.google.com/pricing.
4. Auth Cost Calculator — By Monthly Active Users
What each auth provider actually costs at different user scales. Includes base plan + common add-ons (MFA, phone auth, extra projects).
| MAU | Clerk | Supabase Auth | NextAuth | Firebase |
|---|---|---|---|---|
100 users/mo | $0 Free tier | $0 Free tier | $0 Runs in your app | $0 Free tier |
1,000 users/mo | $0 Free tier (50k limit) | $0 Free tier (50k limit) | $0 Runs in your app | $0 Free allowance |
10,000 users/mo | $0 Free tier. Pro only if you need MFA. | $0 Still free tier | $0 Your existing DB | $0 + SMS Free if email/social only. Phone sign-in billed per SMS. |
100,000 users/mo | ~$1,000+/mo Pro + 50k extra users at $0.02 (before volume discounts) | $25/mo Pro includes 100k MAU | DB cost only Sessions add database load | Per-MAU + SMS Above the free allowance users are billed per MAU; phone adds SMS. |
* Clerk and Supabase figures checked against their pricing pages in October 2026; confirm before you commit. Firebase amounts depend on country and plan. Auth.js has no licence cost.
5. Using Clerk WITH Supabase (Not Instead)
A strong auth pattern: Clerk handles auth UI, organizations, and user management. Supabase handles your database with Row Level Security. Here's the full setup.
Why This Combo Works
Clerk gives you drop-in sign-up/sign-in UI, org management, and SSO that would take weeks to build. Supabase gives you PostgreSQL with RLS — row-level security that scopes every query to the authenticated user. Together, you get enterprise-grade auth UI + database security without building either from scratch.
Step 1 — Set Up Clerk Normally
Install Clerk, wrap your app in ClerkProvider, set up middleware. Follow the standard Clerk setup from the snippets section below.
Step 2 — Activate the Supabase integration in Clerk
In the Clerk Dashboard, open the Supabase integration setup and activate it. Clerk adds the claim Supabase needs to your session tokens and shows you your Clerk domain. You do not need a JWT template; that older method is deprecated.
Step 3 — Add Clerk as a third-party auth provider in Supabase
In the Supabase Dashboard → Authentication → Sign In / Up → Third-Party Auth, add Clerk and paste the Clerk domain from step 2. Supabase now verifies Clerk's tokens directly. Never paste your Supabase JWT secret into Clerk, or a Clerk key into Supabase's JWT secret.
Step 4 — Create a Supabase Client with the Clerk Token
Give the Supabase client a function that returns the current Clerk session token. Supabase calls it on every request.
// lib/supabase/clerk-client.ts
import { createClient } from '@supabase/supabase-js';
import { auth } from '@clerk/nextjs/server';
export function createClerkSupabaseClient() {
return createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
async accessToken() {
return (await auth()).getToken();
},
}
);
}Step 5 — Write RLS Policies Using Clerk's JWT
Since Clerk's JWT uses sub (not Supabase's auth.uid()), your RLS policies reference the JWT directly.
-- Enable RLS on your table
ALTER TABLE todos ENABLE ROW LEVEL SECURITY;
-- Policy: users can only access their own rows
-- auth.jwt()->>'sub' extracts the Clerk user ID from the JWT
CREATE POLICY "Users can CRUD own todos"
ON todos FOR ALL
USING (auth.jwt()->>'sub' = user_id);Common Gotcha
Your user_id column must store the Clerk user ID (e.g., user_2abc123), not a Supabase UUID. When creating rows, set user_id = auth.jwt()->>'sub' in your insert, or give the column that expression as its default. The column must be TEXT.
6. Copy-Paste Setup Snippets
The key config for each — not a full tutorial, just what you actually need to drop into your project.
Clerk — Next.js Setup
1. Install
npm install @clerk/nextjs2. Environment Variables
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_SECRET_KEY=sk_test_...3. Wrap your app — app/layout.tsx
import { ClerkProvider } from '@clerk/nextjs';
export default function RootLayout({ children }) {
return (
<ClerkProvider>
<html><body>{children}</body></html>
</ClerkProvider>
);
}4. Protect routes — middleware.ts (named proxy.ts from Next.js 16)
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';
const isProtected = createRouteMatcher(['/dashboard(.*)']);
export default clerkMiddleware(async (auth, req) => {
if (isProtected(req)) await auth.protect();
});Supabase Auth — Next.js Setup
1. Install
npm install @supabase/supabase-js @supabase/ssr2. Environment Variables
NEXT_PUBLIC_SUPABASE_URL=https://xxxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ...3. Server client util — lib/supabase/server.ts
// lib/supabase/server.ts
import { createServerClient } from '@supabase/ssr';
import { cookies } from 'next/headers';
export async function createClient() {
const cookieStore = await cookies();
return createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{ cookies: {
getAll() { return cookieStore.getAll() },
setAll(list) { try { list.forEach(({ name, value, options }) => cookieStore.set(name, value, options)) } catch {} },
} }
);
}4. Sign up / Sign in
const supabase = await createClient();
// Sign up
const { data, error } = await supabase.auth.signUp({ email, password });
// Sign in
const { data, error } = await supabase.auth.signInWithPassword({ email, password });⚠️ Security — SQL Editor
Always enable RLS on every table. The anon key is public.
ALTER TABLE your_table ENABLE ROW LEVEL SECURITY;
CREATE POLICY "own rows" ON your_table FOR ALL USING (auth.uid() = user_id);NextAuth (Auth.js) — Next.js Setup
1. Install
npm install next-auth@beta2. Environment Variables
AUTH_SECRET=generate-with-openssl-rand-base64-32
AUTH_GITHUB_ID=your-github-client-id
AUTH_GITHUB_SECRET=your-github-client-secret3. Config file — auth.ts
import NextAuth from 'next-auth';
import GitHub from 'next-auth/providers/github';
export const { auth, handlers, signIn, signOut } = NextAuth({
providers: [GitHub()],
});4. Route handler — app/api/auth/[...nextauth]/route.ts
import { handlers } from "@/auth";
export const { GET, POST } = handlers;Firebase Auth — Next.js Setup
1. Install
npm install firebase2. Environment Variables
NEXT_PUBLIC_FIREBASE_API_KEY=AIza...
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=app.firebaseapp.com
NEXT_PUBLIC_FIREBASE_PROJECT_ID=your-project-id3. Firebase config — lib/firebase.ts
import { initializeApp, getApps } from 'firebase/app';
import { getAuth } from 'firebase/auth';
const app = getApps().length ? getApps()[0] : initializeApp(config);
export const auth = getAuth(app);4. Google Sign-In (client component)
import { signInWithPopup, GoogleAuthProvider } from 'firebase/auth';
const provider = new GoogleAuthProvider();
await signInWithPopup(auth, provider);7. What AI Gets Wrong About Auth
When you ask ChatGPT or Claude to "set up auth for my SaaS," here's what it consistently gets wrong.
AI quotes Clerk pricing that is out of date
Every AI model defaults to Clerk for SaaS auth — and rightfully so, the DX is excellent. But models often repeat old pricing (a 10k-user free tier, a paid MFA add-on). Today the free plan covers 50,000 users per app and MFA is part of Pro; the real costs are per-user billing above 50,000 and the $100/mo Enhanced B2B add-on. Always check the pricing page yourself.
AI generates Supabase auth code without RLS policies
The code works perfectly in development. But without Row Level Security policies, your entire database is readable by any authenticated user via the public anon key. AI-generated Supabase auth is a security hole until you add RLS manually.
AI suggests NextAuth for SaaS without mentioning the org/team engineering cost
NextAuth is great for simple login flows. But when you need organizations, team invitations, role-based access, and SSO — standard SaaS requirements — you’re building all of it from scratch. That’s 40–100 hours of engineering that a hosted provider includes.
AI never warns about Firebase phone auth costs for consumer apps
AI recommends Firebase for consumer mobile apps (correctly), but never mentions that phone sign-in is billed per SMS sent, at a rate that depends on the country. A consumer app with heavy phone sign-in can run up a significant monthly bill.
AI doesn’t know about the Clerk + Supabase combined pattern
AI treats Clerk and Supabase Auth as mutually exclusive choices. In reality, the most powerful pattern is using both: Clerk for auth UI and user management, Supabase for database + RLS. This uses Clerk’s native Supabase integration (the older JWT-template method is deprecated) — see our setup guide above.
8. 5 Auth Mistakes Vibe Coders Make
These are the auth patterns that look fine in dev and break in production — or worse, create security holes.
Rolling Your Own Auth
Hashing passwords with bcrypt and managing sessions yourself sounds simple. It isn’t. You’ll miss CSRF protection, session fixation, timing attacks, and secure cookie flags. This is the #1 cause of security breaches.
Skipping RLS When Using Supabase Auth
The anon key is public. If you don’t enable Row Level Security, any user can query any table from the browser. Your entire database is exposed to any user who opens DevTools.
Not Setting Up Email Verification
Users sign up with fake emails. You send transactional emails that never reach anyone. Your bounce rate tanks your sender reputation. Unverified accounts make user management a mess.
Protecting Routes Only on the Client
Checking if (!user) only in a React component means your server routes and API routes are wide open. A direct URL or API call bypasses all of it.
Hardcoding Auth Credentials
NEXT_PUBLIC_ env vars are bundled into your client-side JS and visible to everyone. Service role keys and secret keys committed to GitHub get scraped by bots within minutes.