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. NextAuth with GitHub or Google provider is 30 minutes to working auth with zero ongoing cost.
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 | 10k MAU | 50k MAU | Free (OSS) | 10k/month |
| Pricing After | $25/mo (25k MAU) | $25/mo (project) | Self-host costs only | $0.0055/MAU |
| 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 | ✅ Built-in | ⚠️ TOTP only | ❌ 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 TrapMFA costs $100/mo on the Pro plan
The free tier includes basic password auth and social login. But if you need SMS-based MFA or TOTP for compliance (SOC 2, HIPAA), you need the Pro plan at $25/mo — and the MFA add-on is $100/mo on top of that. A B2B SaaS requiring MFA for enterprise clients is looking at $125/mo minimum before you have a single paying customer.
The Numbers
Free: 10k MAU, no MFA. Pro ($25/mo): 25k MAU + basic MFA. Full MFA add-on: +$100/mo.
Supabase
Pricing TrapThe $25/mo Pro plan is per-project, not per-account
50,000 MAU free sounds generous — and it is for a single project. But the Pro plan ($25/mo) is charged per project. If you have a main app, a staging environment, and a separate admin dashboard, that’s $75/mo. Running microservices with separate Supabase instances? Costs multiply fast.
The Numbers
Free: 50k MAU, 2 projects. Pro: $25/mo per project. 3 projects = $75/mo. Team ($599/mo) covers 1 org.
NextAuth
Pricing Trap“Free” means you build and host everything yourself
NextAuth is open-source, so there’s no license fee. But “free” is misleading. You need hosting for your auth service ($10–20/mo on Railway or Vercel), a database for sessions ($5–15/mo), and your engineering time for every feature Clerk gives you for free: org management, pre-built UI, MFA, user dashboard.
The Numbers
License: $0. Hosting: $10–20/mo. Database: $5–15/mo. Eng time for orgs/MFA/UI: 40–100+ hours.
Firebase
Pricing TrapPhone auth is priced per verification, not per user
Firebase Auth is free for email/password and social login up to 10k MAU. But phone/SMS auth is billed per verification at $0.01–$0.06 per SMS depending on country. A consumer app with 50k phone verifications per month (signups + re-verifications + password resets) can hit $500–$3,000/mo. Nobody mentions this on the marketing page.
The Numbers
Email auth: free to 10k MAU. Phone auth: $0.01–$0.06/SMS. 50k verifications: $500–$3,000/mo.
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 | $10–15/mo Hosting + DB | $0 Free tier |
1,000 users/mo | $0 Free tier (10k limit) | $0 Free tier (50k limit) | $10–15/mo Hosting + DB | $0 Free tier (10k limit) |
10,000 users/mo | $0–$25/mo At free tier limit. Pro for MFA. | $0 Still free tier | $15–25/mo Growing DB + hosting | $0–$55/mo Free if email-only. $55 if phone auth. |
100,000 users/mo | $175+/mo $25 Pro + $100 MFA + overages | $25/mo Pro plan, still under 50k free auth | $25–50/mo Larger DB + compute | $495+/mo $0 email. $495+ if phone auth heavy. |
* Prices as of March 2026. Clerk MFA pricing is for the add-on. Firebase phone auth assumes US SMS rates ($0.01/SMS). NextAuth costs are hosting + database only.
5. Using Clerk WITH Supabase (Not Instead)
The most powerful auth pattern in 2026: 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 — Create a JWT Template in Clerk
In your Clerk Dashboard → JWT Templates → Create New → Choose "Supabase" template. This creates a JWT that Supabase can verify. The template automatically includes the user's Clerk ID as the sub claim.
// Clerk JWT Template payload (auto-generated)
{
"sub": "user_2abc123...",
"iss": "https://clerk.your-app.com",
"role": "authenticated"
}Step 3 — Configure Supabase JWT Secret
Copy the JWT signing key from your Clerk JWT template. In Supabase Dashboard → Settings → API → JWT Secret, paste the Clerk signing key. This tells Supabase to trust JWTs signed by Clerk.
Step 4 — Create a Supabase Client with Clerk Token
Create a utility that initializes the Supabase client with the Clerk session token. This is the key integration point.
// lib/supabase/clerk-client.ts
import { createClient } from '@supabase/supabase-js';
import { auth } from '@clerk/nextjs/server';
export async function createClerkSupabaseClient() {
const { getToken } = await auth();
const supabaseToken = await getToken({
template: 'supabase'
});
return createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
global: {
headers: { Authorization: `Bearer ${supabaseToken}` }
}
}
);
}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 use a database trigger.
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
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';
const isProtected = createRouteMatcher(['/dashboard(.*)']);
export default clerkMiddleware((auth, req) => {
if (isProtected(req)) 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() } } }
);
}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
export { handlers as GET, handlers as POST } from "@/auth";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 always recommends Clerk without mentioning the MFA pricing trap
Every AI model defaults to Clerk for SaaS auth — and rightfully so, the DX is excellent. But none of them mention that MFA (which enterprise clients require) costs $100/mo extra on top of the $25/mo Pro plan. If you’re building for enterprises, you need to budget $125/mo for auth alone.
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 Clerk gives you for $25/mo.
AI never warns about Firebase phone auth costs for consumer apps
AI recommends Firebase for consumer mobile apps (correctly), but never mentions that phone/SMS authentication is billed per verification at $0.01–$0.06 per SMS. A consumer app with heavy phone auth usage can hit $500+/mo in verification costs alone.
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 requires JWT template sync — 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.