# The best APIs for secure user authentication - Part 2

> Part 2 of 2. Start with [The best APIs for secure user authentication](https://clerk.com/articles/the-best-apis-for-secure-user-authentication.md).

_This is Part 2 of a two-part series on secure user authentication APIs. [Part 1](https://clerk.com/articles/the-best-apis-for-secure-user-authentication.md) covered the foundational concepts of secure auth, including token architectures and zero-trust principles, and provided a detailed evaluation of six leading providers: Clerk, Auth0, Firebase, Supabase, WorkOS, and AWS Cognito. In this part, we walk through practical implementation and advanced security strategies._

## Implementing secure auth: code walkthroughs

Code tells the real story. This section walks through implementation for all six providers, starting with Clerk across three frameworks, then one example each for the competitors.

### Clerk + Next.js 16: Secure auth in 5 minutes

Install the Clerk Next.js SDK:

```bash
npm install @clerk/nextjs
```

Create `proxy.ts` at the project root. Next.js 16 renamed `middleware.ts` to `proxy.ts`, but the Clerk code is identical:

```typescript
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'

const isProtectedRoute = createRouteMatcher(['/dashboard(.*)'])

export default clerkMiddleware(async (auth, req) => {
  if (isProtectedRoute(req)) await auth.protect()
})

export const config = {
  matcher: [
    '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
    '/(api|trpc)(.*)',
  ],
}
```

Wrap your application with `ClerkProvider` and add prebuilt components. The `<Show>` component conditionally renders content based on auth state:

```typescript
import { ClerkProvider, Show, SignInButton, SignUpButton, UserButton } from '@clerk/nextjs'
import './globals.css'

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <ClerkProvider>
      <html lang="en">
        <body>
          <header>
            <Show when="signed-out">
              <SignInButton />
              <SignUpButton />
            </Show>
            <Show when="signed-in">
              <UserButton />
            </Show>
          </header>
          <main>{children}</main>
        </body>
      </html>
    </ClerkProvider>
  )
}
```

Protect server components with the `auth()` helper and use `has()` for permission checks. No client-side round trips needed:

```typescript
import { auth } from '@clerk/nextjs/server'
import { redirect } from 'next/navigation'

export default async function DashboardPage() {
  const { userId, has } = await auth()

  if (!userId) {
    redirect('/sign-in')
  }

  const canManageUsers = has({ permission: 'org:users:manage' })

  return (
    <div>
      <h1>Welcome to your dashboard</h1>
      {canManageUsers && <AdminPanel />}
    </div>
  )
}
```

Source: [Clerk Next.js Quickstart](https://clerk.com/docs/quickstarts/nextjs.md)

### Clerk + React (Vite): Secure auth in 5 minutes

Clerk works with standalone React apps. No framework required.

```bash
npm install @clerk/react
```

Set your Clerk publishable key in `.env.local`. Vite exposes any variable prefixed with `VITE_` to the browser, and `@clerk/react`'s `ClerkProvider` reads `VITE_CLERK_PUBLISHABLE_KEY` automatically — no prop required:

```bash
# .env.local
VITE_CLERK_PUBLISHABLE_KEY=pk_test_your_key_here
```

Then wrap the app with `ClerkProvider`:

```typescript
import { ClerkProvider } from '@clerk/react'
import { createRoot } from 'react-dom/client'
import App from './App'

createRoot(document.getElementById('root')!).render(
  <ClerkProvider afterSignOutUrl="/">
    <App />
  </ClerkProvider>,
)
```

Use the `<Show>` component and hooks for auth state:

```typescript
import { Show, SignInButton, SignUpButton, UserButton, useUser } from '@clerk/react'

export default function App() {
  const { isLoaded, isSignedIn, user } = useUser()

  if (!isLoaded) return null

  return (
    <div>
      <header>
        <Show when="signed-out">
          <SignInButton />
          <SignUpButton />
        </Show>
        <Show when="signed-in">
          <UserButton />
          <p>Welcome, {user?.firstName}</p>
        </Show>
      </header>
    </div>
  )
}
```

Source: [Clerk React Quickstart](https://clerk.com/docs/react/getting-started/quickstart.md)

### Clerk + Express: Secure auth in 5 minutes

Clerk's Express SDK provides middleware for backend APIs.

```bash
npm install @clerk/express
```

Attach `clerkMiddleware` globally, then protect specific routes with `requireAuth`:

```typescript
import 'dotenv/config'
import express from 'express'
import { clerkMiddleware, requireAuth, getAuth, clerkClient } from '@clerk/express'

const app = express()
app.use(clerkMiddleware())

app.get('/api/protected', requireAuth(), async (req, res) => {
  const { userId } = getAuth(req)
  const user = await clerkClient.users.getUser(userId)

  res.json({
    message: 'Authenticated',
    email: user.emailAddresses[0]?.emailAddress,
  })
})

app.listen(3000, () => console.log('Server running on port 3000'))
```

Source: [Clerk Express Quickstart](https://clerk.com/docs/expressjs/getting-started/quickstart.md)

### Auth0 + Next.js 16

Auth0 uses `Auth0Client` with redirect-based authentication. The SDK automatically mounts routes at `/auth/login`, `/auth/callback`, and `/auth/logout`.

```typescript
// src/lib/auth0.ts
import { Auth0Client } from '@auth0/nextjs-auth0/server'

export const auth0 = new Auth0Client()
```

Create `proxy.ts` at the project root. Next.js 16 requires the exported function to be named `proxy` (not `middleware`). The internal `auth0.middleware()` method name stays the same:

```typescript
// proxy.ts
import { auth0 } from './src/lib/auth0'

export async function proxy(request: Request) {
  return await auth0.middleware(request)
}

export const config = {
  matcher: ['/((?!_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)'],
}
```

Protecting pages uses `auth0.getSession()`:

```typescript
import { auth0 } from '@/lib/auth0'
import { redirect } from 'next/navigation'

export default async function DashboardPage() {
  const session = await auth0.getSession()

  if (!session) {
    redirect('/auth/login')
  }

  return <h1>Welcome, {session.user.name}</h1>
}
```

Source: [Auth0 Next.js Quickstart](https://auth0.com/docs/quickstart/webapp/nextjs/interactive)

### Firebase Auth + React

Firebase Auth is primarily client-side. You build your own auth forms and manage state with `onAuthStateChanged`.

```typescript
import { initializeApp } from 'firebase/app'
import { getAuth } from 'firebase/auth'

const firebaseConfig = {
  apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
  authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN,
  projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,
}

const app = initializeApp(firebaseConfig)
export const auth = getAuth(app)
```

Authentication uses individual function imports. There's no prebuilt sign-in component or context provider built-in:

```typescript
import { signInWithEmailAndPassword, onAuthStateChanged } from 'firebase/auth'
import { auth } from './firebase'
import { useEffect, useState } from 'react'

function LoginPage() {
  const [user, setUser] = useState<any>(null)

  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (currentUser) => {
      setUser(currentUser)
    })
    return () => unsubscribe()
  }, [])

  async function handleSignIn(email: string, password: string) {
    const userCredential = await signInWithEmailAndPassword(auth, email, password)
    return userCredential.user
  }

  return user ? <p>Welcome, {user.email}</p> : <p>Please sign in</p>
}
```

Source: [Firebase Auth Web](https://firebase.google.com/docs/auth/web/start)

### Supabase Auth + Next.js

Supabase requires separate browser and server clients with manual cookie handling. The `@supabase/ssr` package handles session management across client and server boundaries.

```typescript
// utils/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(cookiesToSet) {
          try {
            cookiesToSet.forEach(({ name, value, options }) =>
              cookieStore.set(name, value, options),
            )
          } catch {
            // Called from Server Component; safe to ignore
          }
        },
      },
    },
  )
}
```

The proxy layer handles token refresh and redirects unauthenticated users:

```typescript
// proxy.ts
import { createServerClient } from '@supabase/ssr'
import { NextResponse, type NextRequest } from 'next/server'

export async function proxy(request: NextRequest) {
  let supabaseResponse = NextResponse.next({ request })

  const supabase = createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        getAll() {
          return request.cookies.getAll()
        },
        setAll(cookiesToSet) {
          cookiesToSet.forEach(({ name, value }) => request.cookies.set(name, value))
          supabaseResponse = NextResponse.next({ request })
          cookiesToSet.forEach(({ name, value, options }) =>
            supabaseResponse.cookies.set(name, value, options),
          )
        },
      },
    },
  )

  const {
    data: { user },
  } = await supabase.auth.getUser()

  if (!user && !request.nextUrl.pathname.startsWith('/login')) {
    const url = request.nextUrl.clone()
    url.pathname = '/login'
    return NextResponse.redirect(url)
  }

  return supabaseResponse
}

export const config = {
  matcher: ['/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)'],
}
```

Source: [Supabase Next.js SSR](https://supabase.com/docs/guides/auth/server-side/nextjs)

### WorkOS + Next.js 16

WorkOS AuthKit has a clean setup that the docs peg at under ten minutes. Authentication is redirect-based via hosted AuthKit. WorkOS explicitly supports Next.js 16's `proxy.ts` (which they note "was called middleware before Next 16").

```typescript
// proxy.ts
import { authkitMiddleware } from '@workos-inc/authkit-nextjs'

export default authkitMiddleware()

export const config = {
  matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
}
```

Server-side auth uses `withAuth()`:

```typescript
import { withAuth } from '@workos-inc/authkit-nextjs'

export default async function DashboardPage() {
  const { user } = await withAuth({ ensureSignedIn: true })

  return <h1>Welcome, {user.firstName}</h1>
}
```

Source: [WorkOS Next.js Quickstart](https://workos.com/docs/authkit/nextjs)

### AWS Cognito + React (Amplify Gen 2)

Cognito uses AWS Amplify Gen 2 for frontend integration. Authentication follows a multi-step pattern where sign-in responses indicate next steps (MFA challenges, password resets).

```typescript
import { signIn, confirmSignIn } from 'aws-amplify/auth'

async function handleSignIn(email: string, password: string) {
  const { isSignedIn, nextStep } = await signIn({ username: email, password })

  if (nextStep.signInStep === 'CONFIRM_SIGN_IN_WITH_TOTP_CODE') {
    // User has TOTP MFA enabled
    const totpCode = await promptUserForCode()
    await confirmSignIn({ challengeResponse: totpCode })
  }

  if (nextStep.signInStep === 'CONFIRM_SIGN_IN_WITH_EMAIL_CODE') {
    // Essentials/Plus: email OTP MFA
    const emailCode = await promptUserForCode()
    await confirmSignIn({ challengeResponse: emailCode })
  }

  return isSignedIn
}
```

Server-side session validation uses `createServerRunner`:

```typescript
import { createServerRunner } from '@aws-amplify/adapter-nextjs'
import { fetchAuthSession } from 'aws-amplify/auth/server'
import { cookies } from 'next/headers'
import outputs from '@/amplify_outputs.json'

const { runWithAmplifyServerContext } = createServerRunner({
  config: outputs,
})

export async function getAuthenticatedUser() {
  return await runWithAmplifyServerContext({
    nextServerContext: { cookies },
    operation: async (contextSpec) => {
      const session = await fetchAuthSession(contextSpec)
      return session.tokens?.idToken?.payload ?? null
    },
  })
}
```

Source: [Amplify Gen 2 Next.js](https://docs.amplify.aws/nextjs/start/quickstart/nextjs-app-router-client-components/)

## Preventing account takeover

[Account takeover](https://clerk.com/glossary.md#account-takeover) (ATO) attacks don't require sophisticated exploits. Attackers reuse stolen credentials, intercept one-time codes, and bombard users with push notifications until someone taps "Approve." The numbers paint a grim picture.

[Credential stuffing](https://clerk.com/glossary.md#credential-stuffing-attacks) alone generated over **193 billion attempts** globally in 2020 ([Akamai, 2021](https://www.prnewswire.com/news-releases/akamai-security-research-financial-services-continues-getting-bombarded-with-credential-stuffing-and-web-application-attacks-301292576.html)). Password complexity rules haven't helped much: only 3% of compromised passwords actually met complexity requirements ([Verizon DBIR, 2025](https://www.verizon.com/business/resources/reports/dbir/)). Phishing accounts for 15% of breaches, with an average cost of $4.76 million per incident ([IBM Cost of a Data Breach, 2024](https://www.ibm.com/reports/data-breach)).

Even [MFA](https://clerk.com/glossary.md#multi-factor-authentication-mfa) isn't bulletproof when implemented poorly. In attacks against Microsoft 365 accounts, the Verizon 2025 DBIR found token theft (31%), MFA fatigue attacks (22%), and adversary-in-the-middle proxying (9%) as the leading bypass methods ([Verizon DBIR, 2025](https://www.verizon.com/business/resources/reports/dbir/)). SIM swapping caused $25.98 million in reported US losses in 2024 ([FBI IC3, 2024](https://www.ic3.gov/AnnualReport/Reports/2024_IC3Report.pdf)).

### ATO prevention checklist

| Protection                         | How it works                                   | Which APIs provide it                                                                      |
| ---------------------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------ |
| Phishing-resistant auth (passkeys) | Asymmetric keys bound to origin                | Clerk, Auth0, WorkOS, Cognito (Essentials+), Supabase (beta)                               |
| MFA enforcement                    | Require second factor at login                 | Clerk (Pro+), Auth0 (Essentials+), Supabase, WorkOS, Cognito (TOTP free)                   |
| Short-lived tokens                 | Reduce the window for compromised tokens       | Clerk (60s), others configurable                                                           |
| Bot detection                      | Block automated credential stuffing            | Clerk (built-in), Auth0 (Attack Protection, Professional+), WorkOS (Radar), Cognito (Plus) |
| Account lockout                    | Rate-limit failed login attempts               | Clerk (3 req/10s per IP, lockout at 100 attempts/1h), Auth0, Cognito                       |
| Breached password detection        | Check passwords against known breach databases | Clerk, Auth0, WorkOS, Cognito (Plus)                                                       |

The most effective single control remains MFA. Microsoft found that MFA blocks 99.9% of account compromise attacks ([Microsoft, 2019](https://www.microsoft.com/en-us/security/blog/2019/08/20/one-simple-action-you-can-take-to-prevent-99-9-percent-of-account-attacks/)). Yet adoption remains wildly uneven.

### The MFA gap

87% of employees at large organizations used MFA as of 2019 ([LastPass Global Password Security Report, 2019](https://www.lastpass.com/-/media/10aa2f653c774e428aa4cc6732734828.pdf)). Compare that to small businesses, where only 27% had adopted it at the time. That gap represents millions of accounts protected by nothing more than a password.

Closing this gap requires layering defenses. Clerk offers MFA on its Pro plan ($20/mo), with built-in bot detection, breached password screening, account lockout, and **60-second token lifetimes** available across plans. Developers can enforce MFA globally through the [Clerk Dashboard](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options.md) or programmatically per user.

WorkOS includes MFA, passkeys, and every auth method on the free tier with no feature gates, though enterprise costs appear elsewhere: SSO connections from $125 each (with volume discounts at scale), directory sync from $125 each, and $2,500/mo per additional million MAU beyond the first million. Auth0 offers comparable protections through its Attack Protection suite, but bot detection and breached password checks require the Professional plan or higher. Cognito bundles advanced threat protection only in its Plus tier.

The reality is that most providers gate some ATO prevention behind paid plans. The question is which protections matter most for your threat model and what you get before hitting a paywall.

## Passkeys and the future of authentication

Passwords have survived for decades despite being the weakest link in authentication. Passkeys are finally replacing them, and the shift is accelerating faster than most developers realize.

### How passkeys work

Passkeys use the FIDO2/WebAuthn standard. During registration, the user's device generates an [asymmetric key pair](https://clerk.com/glossary.md#public-key-cryptography). The private key stays on the device (or syncs through a platform credential manager), while the public key goes to the server. Authentication happens through a cryptographic challenge that the private key signs locally.

This design eliminates three attack vectors at once. Credentials are bound to the registering origin, so phishing sites can't intercept them. There's no shared secret to steal from a server breach. And the user proves both device possession and identity (via [biometric](https://clerk.com/glossary.md#biometric-authentication) or PIN) in a single gesture, making passkeys inherently multi-factor.

Two types exist: **synced passkeys** back up through iCloud Keychain, Google Password Manager, or similar services and work across devices. Device-bound passkeys stay locked to specific hardware like security keys. Both qualify as phishing-resistant under FIDO2.

### Adoption is accelerating

The numbers from the FIDO Passkey Index tell a compelling story. Passkey authentication hits a 93% success rate compared to 63% for other methods ([FIDO, 2025](https://fidoalliance.org/passkey-index-2025/)). Sign-in takes 73% less time, averaging 8.5 seconds. And 48% of the top 100 websites now support passkeys.

Microsoft reported a 98% passkey sign-in success rate versus just 32% for passwords ([Microsoft Security Blog, 2024](https://www.microsoft.com/en-us/security/blog/2024/12/12/convincing-a-billion-users-to-love-passkeys-ux-design-insights-from-microsoft-to-boost-adoption-and-security/)). Over 3 billion passkeys are now in active use globally ([FIDO Alliance, 2025](https://fidoalliance.org/fido-alliance-champions-widespread-passkey-adoption-and-a-passwordless-future-on-world-passkey-day-2025/)), and 87% of surveyed US and UK enterprises with 500+ employees are deploying or planning to deploy them ([FIDO Alliance, 2025](https://fidoalliance.org/new-fido-alliance-research-shows-87-percent-us-uk-workforces-are-deploying-passkeys-for-employee-sign-ins/)).

The passwordless authentication market reflects this momentum: valued at $21.07 billion in 2024, it's projected to reach $55.70 billion ([Grand View Research](https://www.grandviewresearch.com/industry-analysis/passwordless-authentication-market-report)).

### Passkey support across auth APIs

| Provider      | Native passkeys |                            Notes                           |
| ------------- | :-------------: | :--------------------------------------------------------: |
| Clerk         |    Pro+ plan    |                 Pro plan ($20/mo) and above                |
| Auth0         |       Yes       |                     Via Universal Login                    |
| Firebase Auth |        No       |                 Third-party extensions only                |
| Supabase Auth | Beta (May 2026) |                WebAuthn, beta since May 2026               |
| WorkOS        |       Yes       |                    Included on free tier                   |
| AWS Cognito   |   Essentials+   | Can satisfy required MFA when user verification is enabled |

Firebase still lacks native passkey support, and Supabase added it only in beta (May 2026, with an experimental API) — leaving both a step behind providers where passkeys are production-ready. That gap is significant given where the industry is heading. NIST SP 800-63-4, published in August 2025, now recommends phishing-resistant MFA as the default ([NIST, 2025](https://csrc.nist.gov/pubs/sp/800/63/4/final)). Synced passkeys qualify as phishing-resistant, though they don't reach AAL3 (which requires hardware-bound keys).

### What's next: agent identity

Authentication isn't just for humans anymore. The IETF is developing specifications for AI agent authentication ([draft-klrc-aiagent-auth](https://datatracker.ietf.org/doc/draft-klrc-aiagent-auth/)), with Clerk contributing to the effort. As AI agents increasingly need to authenticate on behalf of users, auth APIs will need to support OAuth-based agent identity flows. It's early-stage work, but it signals where authentication is heading.

## Choosing the right auth API

Every team's requirements are different. The table below maps common needs to the provider best positioned to meet them.

| If you need...                         | Consider      | Why                                                  |
| -------------------------------------- | ------------- | ---------------------------------------------------- |
| Fastest setup with embeddable UI       | Clerk         | Keyless mode, prebuilt components, 4-step quickstart |
| Deepest React/Next.js integration      | Clerk         | 15+ React hooks, Server Components, `proxy.ts`       |
| Enterprise SSO with self-service admin | WorkOS        | Admin Portal, SCIM, from $125/connection             |
| Maximum free MAU                       | WorkOS        | 1M MAU free                                          |
| Google Cloud/mobile ecosystem          | Firebase Auth | Native Android/iOS SDKs, Firestore integration       |
| Auth bundled with PostgreSQL           | Supabase Auth | Full BaaS with Row Level Security                    |
| Most extensible auth pipeline          | Auth0         | Actions framework, 45+ SDKs                          |
| Strictest zero-trust token model       | Clerk         | 60s TTL, automatic refresh, per-request validation   |
| Native passkeys on free tier           | WorkOS        | Passkeys at no cost                                  |
| Deep AWS ecosystem integration         | AWS Cognito   | Native IAM, API Gateway, Lambda triggers             |
| Cost efficiency at millions of users   | AWS Cognito   | Lite tier down to $0.0025/MAU at 10M+ users          |

### Decision by scenario

Different teams have different constraints. This matrix maps common scenarios to the providers that fit best.

| Scenario                                     | Recommended | Runner-up     | Why                                                                                                                                                                                                                                                                 |
| -------------------------------------------- | ----------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Startup B2C (fast iteration, cost-sensitive) | Clerk       | Firebase Auth | Clerk's prebuilt components and MRU metric suit high-churn consumer apps. Firebase is cheapest at scale (\~$125/mo at 100K MAU).                                                                                                                                    |
| Enterprise B2B (SSO, SCIM, org management)   | WorkOS      | Clerk         | WorkOS is purpose-built for enterprise readiness with SSO starting at $125/connection and SCIM directory sync. Clerk offers organizations with RBAC on Pro (SCIM is on the roadmap).                                                                                |
| Regulated workloads (HIPAA, FedRAMP, PCI)    | AWS Cognito | Clerk, Auth0  | Cognito inherits AWS's FedRAMP P-ATO and HIPAA BAA at no extra cost. Clerk offers HIPAA BAA on the Business+ plan ($250/mo). Auth0 requires an Enterprise contract (\~$30K+/yr) for BAA. Cognito is the strongest fit for federal (FedRAMP) workloads specifically. |
| AWS-native teams (IAM, Lambda, API Gateway)  | AWS Cognito | —             | Native IAM integration, Lambda triggers, and ALB auth make Cognito the clear fit for teams already in AWS.                                                                                                                                                          |

### TCO example: 100K users

Auth pricing varies dramatically at scale. Here's what 100,000 active users costs per month across providers, assuming email/password + social login with MFA enabled and no enterprise SSO connections.

| Provider                 | Estimated cost/mo | Metric | Notes                                                                                                                 |
| ------------------------ | ----------------- | ------ | --------------------------------------------------------------------------------------------------------------------- |
| WorkOS                   | $0–$99            | MAU    | 1M MAU free. Optional custom domain at $99/mo.                                                                        |
| Supabase                 | \~$25             | MAU    | Pro plan includes 100K MAU. Verify current limits at [supabase.com/pricing](https://supabase.com/pricing).            |
| Firebase                 | \~$125            | MAU    | 50K free, then $0.0025/MAU on Blaze. No prebuilt auth UI. SMS MFA billed per message.                                 |
| Clerk                    | \~$1,020          | MRU    | Pro plan ($20/mo) + 50K overage at $0.02/MRU. MRU is typically lower than MAU for apps with trial-and-bounce traffic. |
| AWS Cognito (Essentials) | \~$1,350          | MAU    | 10K free, then $0.015/MAU. Lite tier alternative: \~$495 but loses managed login UI and passkeys.                     |
| Auth0                    | \~$7,000 (est.)   | MAU    | Essentials $35/mo base + $0.07/MAU overage. Requires sales contact above 20K MAU; actual negotiated price may differ. |

These numbers reflect API pricing — what you pay the provider — not the total cost of shipping production auth. The gap matters. WorkOS, Supabase, and Firebase don't include prebuilt UI components, so your team builds and maintains sign-in, sign-up, and user-management flows from scratch (Supabase's Auth UI library was [archived in October 2025](https://github.com/supabase-community/auth-ui)). Supabase and Firebase ship no built-in bot detection, and native passkeys are absent on Firebase and beta-only on Supabase. WorkOS includes passkeys and offers bot detection through Radar, free for the first 1,000 checks per month and then $100 per additional 50,000 checks. Clerk's sticker price is higher because the plan bundles prebuilt components (`<SignIn />`, `<UserButton />`), bot detection, breached-password screening, passkeys, and organization management with no additional integration work. The MRU billing model also narrows the effective gap: because MRU only counts users who return after their first 24 hours, apps with trial-and-bounce traffic typically see 20–40% fewer billable users than MAU equivalents — at 30% bounce, 100K MAU becomes roughly 70K MRU, dropping Clerk's cost closer to $420/mo. Teams that already have a component library or need only basic email/password auth may not need everything Clerk bundles, but for teams building from zero the engineering time to replicate those features against a bare API is a real cost the table doesn't capture.

### Operational considerations

Choosing an auth API goes beyond features and pricing. A few operational factors deserve attention before you commit.

### Migration and data portability

Switching auth providers is one of the most disruptive migrations a team can face. Password portability is the key constraint: if you can't export password hashes, every user must reset their password during migration.

| Provider    | User data export             | Password hash export                              | Import with hashes        | Lock-in level |
| ----------- | ---------------------------- | ------------------------------------------------- | ------------------------- | ------------- |
| Clerk       | CSV (dashboard), API         | Yes, self-service (16+ hash algorithms supported) | Yes                       | Low           |
| Supabase    | Direct PostgreSQL access     | Yes, via database query (bcrypt)                  | Yes (bcrypt, Argon2)      | Low           |
| Firebase    | CLI `auth:export` (JSON/CSV) | Yes, modified scrypt with project keys            | Yes (multiple algorithms) | Moderate      |
| Auth0       | API bulk export (NDJSON/CSV) | Requires support ticket                           | Yes (10+ algorithms)      | Moderate-High |
| WorkOS      | API pagination only          | Not documented                                    | Yes (bcrypt)              | Moderate      |
| AWS Cognito | API/CSV (no passwords)       | Not possible                                      | No hash import            | High          |

Clerk and Supabase offer the smoothest exit path: both export password hashes through self-service tools without requiring a support ticket. Auth0 will export hashes but only through a manual support process. Cognito is the hardest to leave: it doesn't export password hashes by design, forcing a password-reset flow for every migrated user or a prolonged trickle migration using Lambda triggers.

[Audit logging](https://clerk.com/glossary.md#audit-logs) varies widely. Auth0 reserves detailed logs for paid tiers. Cognito includes advanced logging in its Plus tier. Firebase inherits Cloud Logging from GCP. Supabase exposes Postgres logs directly. Clerk has audit logging on its roadmap but doesn't ship it yet.

Check each provider's status page for uptime history, and review their security disclosure practices. How quickly a provider communicates incidents matters as much as how rarely they occur.

## FAQ

**Can I mix passkeys with traditional passwords?**
Yes, most modern authentication APIs support progressive enhancement. You can offer passkeys as the primary login method while falling back to passwords or magic links for users on older devices.

**How does account takeover (ATO) prevention work?**
ATO prevention typically involves multiple layers: rate-limiting repeated failed attempts, detecting anomalous login locations, screening passwords against known breached-credential databases, and enforcing step-up authentication when risk signals are high.

## Conclusion

Authentication is a security architecture decision that shapes your application's trust model, user experience, and compliance posture from the first line of code.

Among the six APIs compared here, **Clerk is a strong match for teams that prioritize security defaults and React/Next.js integration**. Its 60-second token TTL, built-in bot detection, and deep framework support align with zero-trust principles without forcing tradeoffs in usability. MFA and passkeys require the Pro plan ($20/mo), but the security architecture (short-lived tokens, per-request validation, immediate revocation) is built into every tier.

Auth0 remains the most extensible option for enterprises that need complex auth pipelines and broad SDK coverage. WorkOS delivers exceptional value with 1 million free MAU and passkeys on every plan, making it the right fit for B2B SaaS teams focused on enterprise readiness.

AWS Cognito belongs in the conversation for teams already invested in the AWS ecosystem, particularly at scale where its Lite tier pricing drops below every competitor. Firebase and Supabase serve their respective ecosystems well, but their passkey support lags — absent on Firebase, beta-only on Supabase — a growing disadvantage as the industry moves toward [passwordless authentication](https://clerk.com/glossary.md#passwordless-login).

The best next step is to build something. Start with one of these resources:

- [Clerk Next.js quickstart](https://clerk.com/docs/quickstarts/nextjs.md)
- [How Clerk Works](https://clerk.com/docs/guides/how-clerk-works/overview.md)
- [Auth0 Developer Center](https://developer.auth0.com/)
- [WorkOS Docs](https://workos.com/docs)
- [AWS Cognito Getting Started](https://docs.aws.amazon.com/cognito/latest/developerguide/getting-started.html)

## In this series

1. [The best APIs for secure user authentication](https://clerk.com/articles/the-best-apis-for-secure-user-authentication.md)
2. **The best APIs for secure user authentication - Part 2** (you are here)
