← Following the path? See every step

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.

⚠️Don’t use NextAuth (too much plumbing for SaaS features) or roll your own.
🛒

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.

⚠️Clerk is great but adds complexity when you need RLS on every table.
📱

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.

⚠️Supabase social auth requires more config per provider.
🔧

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.

⚠️Don’t pay for Clerk or Firebase just to gate an internal tool.

2. Clerk vs Supabase Auth vs NextAuth vs Firebase Auth

Full comparison across the dimensions that actually matter for vibe-coded apps.

FeatureClerkSupabaseNextAuthFirebase
Free Tier50k users/app50k MAUFree (OSS)Free allowance
Pricing AfterPro + $0.02/user over 50kPro from $25/mo (100k MAU)Your hosting onlyPer 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 ForSaaS, B2BMarketplace, data-heavyInternal tools, simple appsConsumer 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 Trap

Free 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 Trap

Free 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 Trap

Phone 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).

MAUClerkSupabase AuthNextAuthFirebase

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/nextjs

2. 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/ssr

2. 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@beta

2. Environment Variables

AUTH_SECRET=generate-with-openssl-rand-base64-32
AUTH_GITHUB_ID=your-github-client-id
AUTH_GITHUB_SECRET=your-github-client-secret

3. 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 firebase

2. Environment Variables

NEXT_PUBLIC_FIREBASE_API_KEY=AIza...
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=app.firebaseapp.com
NEXT_PUBLIC_FIREBASE_PROJECT_ID=your-project-id

3. 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.

01

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.

02

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.

03

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.

04

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.

05

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.

01

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.

SolutionUse Clerk, Supabase Auth, or NextAuth. Auth is solved. Your job is to ship your product.
02

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.

SolutionEvery table needs RLS enabled AND a policy. No policy = no access. Bad policy = everyone can see everything.
03

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.

SolutionEnable email confirmation in your auth provider. For Supabase: Dashboard → Auth → Email → Confirm email ON.
04

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.

SolutionProtect routes in middleware or in the server component using the server-side auth check. Never trust the client.
05

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.

SolutionSecret keys stay in .env.local (gitignored) and your hosting provider’s env vars. Never prefix secret keys with NEXT_PUBLIC_.

9. Tools & Resources

Stop Evaluating

Build your auth as part of a real project

Follow the path