Skip to main content
Articles

The best APIs for secure user authentication

Author: Roy Anger
Published:

Introduction

Twenty-two percent of confirmed data breaches in 2024 started with stolen credentials (Verizon DBIR, 2025). In basic web application attacks, that number climbs to 88%. When credentials fail, the average cost lands at $4.67 million (IBM Cost of a Data Breach, 2025).

These aren't hypothetical risks. They're the direct result of authentication systems that were bolted on as an afterthought, chosen for developer convenience rather than security architecture.

Most teams pick an auth API the same way they pick a UI library: read a tutorial, spin up a quickstart, ship it. The token lifetimes, the signing algorithms, the session validation model? Those details get buried under "it works in dev." But authentication is the load-bearing wall between your users' data and every attacker scanning for weak entry points.

The gap between "has login" and "has secure login" is wide. A provider that issues tokens valid for 24 hours handles risk differently than one that expires them in 60 seconds. A provider that supports passkeys eliminates an entire class of phishing attacks that passwords can't survive. A provider that validates sessions on every request aligns with zero-trust principles; one that trusts a cookie until it expires doesn't.

This guide evaluates six authentication APIs through a security-first lens. You'll find the protocols behind secure auth broken down and compared, zero-trust principles applied to API selection criteria, and implementation code across Next.js, React, and Express for each provider. The six APIs covered are Clerk, Auth0, Firebase Authentication, Supabase Auth, WorkOS, and AWS Cognito.

Whether you're selecting an auth provider for a greenfield project or pressure-testing your current setup against 2026 security standards, the goal here is the same: give you the technical detail to make a decision grounded in how these systems actually protect credentials, not just how quickly they let you render a login form.

What makes an auth API "secure"?

Before comparing specific providers, it helps to nail down what "secure" actually means when applied to an authentication API. Marketing pages will claim security across the board. The criteria below strip that back to measurable, verifiable properties.

CriteriaWhat to look forWhy it matters
Token architectureShort-lived tokens, automatic refresh, RS256/ES256 signingLimits the exposure window if tokens are intercepted
Protocol supportOAuth 2.0/OIDC, SAML, FIDO2/WebAuthnCovers SSO, enterprise federation, and passwordless flows
Passkey supportNative WebAuthn integration, discoverable credentialsPhishing-resistant auth that eliminates credential stuffing entirely
Compliance certificationsSOC 2 Type II, GDPR, HIPAA, PCI DSS 4.0Required for enterprise, healthcare, and payment applications
Zero-trust alignmentPer-request verification, continuous session validationMatches NIST SP 800-207 principles for modern architectures (NIST, 2020)
Bot and ATO preventionRate limiting, brute-force detection, anomaly detectionDefends against credential stuffing, which exceeded 193 billion attempts globally in 2020 (Akamai, 2021)
Developer experienceSDK depth, prebuilt components, setup complexityFaster implementation reduces the time your app sits exposed with half-wired auth
Free tier and pricingGenerous free tier, transparent scaling costsAllows real evaluation without financial commitment or sales calls

These criteria are drawn from the OWASP Authentication Cheat Sheet (OWASP, 2024), NIST SP 800-207, and the practical realities of shipping auth in production. Each provider section later in this guide maps directly back to this table.

No single criterion is enough on its own. A provider with SOC 2 Type II but 24-hour token lifetimes has a different risk profile than one with 60-second tokens and no SAML support. The right choice depends on your threat model.

Understanding the auth protocol stack

One of the most common points of confusion in authentication: there's no such thing as "logging in with OAuth." OAuth 2.0 is an authorization framework. It handles permissions, not identity. When you click "Sign in with Google," you're actually using OpenID Connect (OIDC), which is an identity layer built on top of OAuth 2.0.

That distinction matters when you're evaluating auth APIs, because it determines what your tokens contain, how they're validated, and what guarantees you get about the person on the other end of the request.

OAuth 2.0, OIDC, and OAuth 2.1

OAuth 2.0 (RFC 6749)OpenID Connect 1.0OAuth 2.1 (Draft-15)
PublishedOctober 2012February 2014March 2026 (Internet-Draft)
PurposeAuthorization (access delegation)Authentication (identity verification)Authorization with security defaults
Key artifactAccess tokenID token (JWT with user claims)Access token
Implicit flowAllowedAllowedRemoved
ROPC flowAllowedN/ARemoved
PKCEOptionalOptionalRequired for all flows
Discovery endpointNoYes (/.well-known/openid-configuration)No

OAuth 2.0 was published as RFC 6749 in 2012. It defined the Authorization Code, Implicit, Resource Owner Password Credentials (ROPC), and Client Credentials flows. Since then, security research has shown that Implicit and ROPC are unsafe for most use cases. RFC 9700, published in January 2025, formalized these findings as OAuth 2.0 Security Best Current Practice (IETF, 2025).

PKCE (RFC 7636) was originally designed to protect mobile apps from authorization code interception. It works by generating a random code verifier, hashing it with S256, and sending the hash in the authorization request. The token exchange then requires the original verifier. RFC 9700 mandates PKCE for all public clients and recommends it for confidential clients (IETF, 2025). The OAuth 2.1 draft goes further, requiring PKCE for all clients using the authorization code flow, with a narrow exception for confidential clients that demonstrate alternative injection mitigation.

DPoP (RFC 9449) goes a step further. It sender-constrains access tokens by binding them to a cryptographic key held by the client. If a token is stolen in transit, it can't be replayed from a different device. Adoption is still early, but it's worth checking whether your auth provider supports it.

OpenID Connect sits on top of OAuth 2.0 and adds the piece OAuth deliberately left out: identity. When a user authenticates through OIDC, the authorization server returns an ID token, a signed JWT containing claims like sub (subject identifier), email, name, and aud (audience). The discovery endpoint at /.well-known/openid-configuration publishes the issuer's public keys, token endpoints, and supported scopes, which lets clients verify tokens without pre-shared secrets.

OAuth 2.1 (draft-15, March 2026) consolidates the best practices from RFC 9700 into a single specification. It removes Implicit and ROPC entirely and requires PKCE for all flows. It's still an Internet-Draft, not a finalized RFC. OAuth 3.0 doesn't exist.

Here's the practical takeaway: if your application has user login, you're using OIDC. All six APIs evaluated in this guide implement OIDC. The differences lie in how they handle token lifetimes, session management, and the security defaults they ship with.

Zero-trust authentication principles

Zero trust is an architecture model defined in NIST SP 800-207 (NIST, 2020) that operates on a single premise: no request is inherently trusted, regardless of where it originates.

Four principles from the framework apply directly to authentication API selection:

  1. "Never trust, always verify." Every request must carry proof of identity. A session cookie set at login and trusted for hours doesn't meet this bar. Per-request token validation does.

  2. Per-session access with least privilege. Users and services should receive the minimum permissions needed for their current task. Tokens should encode scopes tightly, not grant blanket access.

  3. Dynamic policy evaluation. Access decisions should factor in real-time signals: device posture, location anomalies, time since last authentication. Static role checks are necessary but not sufficient.

  4. Continuous monitoring. Session validity should be re-evaluated throughout its lifetime, not just at the moment of login. Revocation must propagate quickly.

What does this mean for choosing an auth API? Token lifetime is the clearest differentiator. A provider that issues tokens valid for 24 hours creates a 24-hour window where a stolen token works. A provider with 60-second token lifetimes and automatic background refresh shrinks that window to under a minute. The architectural difference is significant.

Session state matters too. Some providers store session validity on the server and check it on every request. Others encode everything in the token itself and trust it until expiration. The first model lets you revoke access instantly. The second can't.

Passkeys and FIDO2/WebAuthn fit squarely into zero-trust thinking. Phishing-resistant authentication removes the most exploited attack vector (stolen credentials) from the equation entirely. No password means no password to steal.

Sixty-three percent of organizations worldwide have now implemented a zero-trust strategy (Gartner, 2024). OMB Memorandum M-22-09, issued under Executive Order 14028, requires phishing-resistant MFA for federal agency staff, contractors, and partners accessing federal systems (OMB, 2022).

The next section applies these principles directly, comparing how each of the six auth APIs handles token lifetimes, session validation, passkey support, and revocation.

The 6 best APIs for secure user authentication

How we selected these providers

These 6 APIs were chosen based on specific criteria: developer-first API design, modern protocol support (OAuth 2.0/OIDC, FIDO2), first-party framework SDKs for Next.js, React, and Express, active maintenance with 2025 and 2026 updates, and relevance across B2C, B2B, and enterprise use cases. They represent the most commonly evaluated options for teams building new applications. Other notable providers (Stytch, Descope, Kinde, FusionAuth, Microsoft Entra External ID) exist but fall outside this comparison's scope due to narrower adoption, self-hosted focus, or enterprise-only positioning.

A note on pricing models: MRU vs MAU

All prices in this article are in USD unless otherwise noted.

Before comparing free tiers, it's worth understanding how providers count users. Most competitors use Monthly Active Users (MAU), which counts anyone who authenticates during the month. Clerk uses Monthly Retained Users (MRU): "a user who visits your app in a given month at least one day after signing up" (Clerk Pricing). Users who sign up and never return don't count toward your MRU limit. This distinction makes Clerk's 50,000 free tier effectively more generous for apps with typical trial-and-bounce patterns.

Feature comparison

FeatureClerkAuth0Firebase AuthSupabase AuthWorkOSAWS Cognito
Free tier50,000 MRU25,000 MAU50,000 MAU50,000 MAU1,000,000 MAU10,000 MAU
Native passkeysPro+ planEssentials+
SOC 2 Type IIVia Google CloudVia AWS
GDPRDPF certifiedEU regionsUS servers onlyEU region optionDPA available
HIPAABusiness+Via Google CloudWith add-onEnterpriseBAA required
Prebuilt UIEmbeddable componentsRedirect-basedArchived (Oct 2025)Redirect-basedRedirect-based
Next.js 16 (proxy.ts)Day-one supportYes (--legacy-peer-deps)SSR via FirebaseServerApp (Edge incompatible)No proxy.ts guidance
MFAPro+ planEssentials+Upgrade requiredTOTP free; email Essentials+
Bot detectionBuilt-in (Cloudflare)Attack Protection (Professional+)Device fingerprintingPlus plan only
Enterprise SSOSAML + OIDC (Pro+)SAML + OIDC (B2B Essentials+, 3-5 connections)Upgrade to Identity PlatformPaid add-onSAML + OIDC (core strength)SAML + OIDC (50 MAU free)

Sources: Official documentation for each provider, verified March 2026

Evaluation rating criteria

The token architecture comparison below uses Strong, Moderate, and Weak ratings for zero-trust alignment. Here's what each means:

RatingCriteria
StrongSecure by default with no configuration required. Short-lived tokens, built-in protections, or zero-trust alignment out of the box.
ModerateSecure configuration available but requires manual setup or non-default settings. Configurable token lifetimes, optional protections.
WeakLimited security controls, long fixed token lifetimes, or missing protections that require third-party solutions.

These ratings reflect default configurations with the following assumptions: free-tier plan unless noted, authorization code flow with PKCE, latest stable SDK version as of March 2026, and Next.js App Router for framework comparisons. All providers can be hardened with custom configuration. Your priorities will differ based on stack, scale, and compliance requirements, so weight the dimensions that matter most to your use case.

Token architecture comparison

Token TTLs aren't directly comparable across providers because they use different token types and architectural models. This table normalizes the comparison by showing equivalent constructs.

DimensionClerkAuth0Firebase AuthSupabase AuthWorkOSAWS Cognito
Session token TTL60s (auto-refresh at 50s)
Access token TTL (default)24h (86,400s, configurable)1h (3,600s, configurable)Configurable1h (configurable, 5min to 24h)
ID token TTL10h (36,000s, configurable)1h (fixed)Configurable
Refresh behaviorAutomatic (50s cycle, no user action)Refresh token rotation (configurable)Automatic background refreshAutomatic server-side refreshSession-basedRefresh token (30d default)
Revocation modelImmediate (client token on Clerk domain)Token revocation endpointFirebase Admin SDKSupabase Admin APISession invalidationGlobal sign-out / token revocation
Zero-trust alignmentStrongModerateWeakModerateModerateModerate

Clerk's 60-second session token represents a fundamentally different architecture from Auth0's 24-hour access token. Clerk separates the session token (short-lived, used for per-request validation) from the client token (long-lived, HttpOnly, stored on the application's domain as the source of truth). A stolen Clerk session token expires in under 60 seconds. A stolen Auth0 access token remains valid for up to 24 hours unless explicitly revoked (How Clerk Works; Auth0 Docs). Auth0's token lifetimes are fully configurable and can be shortened to match stricter requirements.

Clerk

Clerk uses a hybrid auth model that separates the session token (60s TTL, RS256-signed, auto-refreshed every 50 seconds) from a long-lived client token (HttpOnly cookie on the application's domain). The client token serves as the source of truth, which means session tokens can be validated without database calls while revocation happens immediately at the source (How Clerk Works).

Security certifications include SOC 2 Type II, HIPAA (Business+ plan), and GDPR compliance via the EU-US Data Privacy Framework (DPF certification). Bot detection runs through Cloudflare's CDN with CAPTCHA challenges for suspected bots. Sign-in attempts are rate-limited to 3 per 10 seconds per IP (system limits), meaning brute-force attacks are throttled well before reaching the account lockout threshold of 100 failed attempts and a 1-hour cooldown (bot protection; user lockout).

The developer experience starts with a 4-step quickstart and keyless mode for instant setup. Clerk ships SDKs for 15+ platforms with prebuilt, a11y-optimized components like <SignIn /> and <UserButton /> (auth strategies).

For B2B teams, Clerk provides native organization management (free tier includes basic orgs with up to 20 members), RBAC with the has() helper, and enterprise SSO (SAML + OIDC, Pro+ with 1 connection included). SCIM provisioning is on Clerk's roadmap but not yet available. The platform now manages 200M+ users across 15,000+ apps, backed by a $50M Series C led by Menlo Ventures and Anthropic (Series C).

Considerations: Clerk is fully managed with no self-hosting option. Native passkeys and MFA require the Pro plan ($20/mo). Clerk's strongest framework support targets React and Next.js; teams using Vue, Angular, or non-JavaScript stacks will find fewer prebuilt components compared to Auth0's 45+ SDKs. At high scale, Clerk's per-MRU pricing can exceed Cognito's Lite tier, which drops to $0.0025/MAU above 10 million users.

Auth0 (by Okta)

Auth0 ships 45+ SDKs across 12 languages and provides the Actions framework for injecting custom logic at any point in the authentication pipeline. Compliance certifications cover SOC 2, ISO 27001/27017/27018, PCI DSS, and HIPAA (Auth0 Compliance).

Native passkey and WebAuthn support runs through Universal Login. Token defaults ship with a 10-hour ID token lifetime and a 24-hour access token lifetime, both configurable to shorter values (Auth0 ID Token; Auth0 Access Token).

Attack Protection bundles bot detection, suspicious IP throttling, brute force protection, and breached password detection. These features are available on Professional+ or as an Enterprise add-on (Auth0 Attack Protection).

Considerations: Auth0's pricing has drawn criticism for its "growth penalty," where costs scale steeply as user counts climb. The free tier lacks MFA, bot detection, and RBAC. MFA becomes available on Essentials+, but SMS and Push MFA require the Professional plan with the Enterprise MFA Lite add-on. Authentication is redirect-based: users leave your app to sign in.

Firebase Auth

Firebase Authentication offers 50,000 MAU free with tight Google ecosystem integration and strong mobile SDKs for Android and iOS. Auth state persists via IndexedDB (not localStorage) since SDK v4.12.0 (Firebase Auth Persistence).

Compliance flows through Google Cloud: SOC 1/2/3, ISO 27001 (Firebase Privacy). Official SSR support arrived via FirebaseServerApp in May 2024, giving server-rendered apps a first-party path to session handling (Firebase SSR Blog). However, FirebaseServerApp doesn't work with the Next.js Edge Runtime (GitHub Issue #8299). Community libraries like next-firebase-auth-edge fill that gap for App Router integration.

Considerations: Firebase has no native passkey support and no prebuilt React UI components. Data residency is limited to US servers, which raises GDPR concerns for EU-facing apps. ID tokens carry a fixed 1-hour TTL that can't be configured.

Supabase Auth

Supabase Auth is open-source, built on GoTrue, and offers 50,000 MAU free. It's PostgreSQL-native, meaning auth and data live in the same database. Row Level Security policies handle authorization at the database level, eliminating a whole category of access control bugs (Supabase Auth).

The full BaaS platform bundles auth, database, storage, and edge functions. EU region hosting is available for teams with data residency requirements (Supabase SOC 2).

Considerations: The Auth UI library was archived in October 2025 after entering maintenance mode in February 2024 (GitHub: auth-ui). There's no native passkey support. Free tier projects pause after 7 days of inactivity, which can disrupt development workflows.

WorkOS

WorkOS offers 1,000,000 MAU free for full authentication through AuthKit. Every auth method ships on the free tier: email, social login, magic auth, MFA, and passkeys. No feature gates (WorkOS Pricing).

Enterprise SSO is the platform's core strength. Connections are priced at $125 each, with SCIM directory sync and an Admin Portal that lets customers configure SSO themselves. The Next.js 16 integration is clean: 4 steps, explicit proxy.ts support (WorkOS Next.js Quickstart).

Considerations: Authentication is redirect-based (users leave your app to sign in via hosted AuthKit). The platform leans enterprise and B2B. React SPA integration is less documented than the Next.js path.

AWS Cognito

Cognito uses a three-tier pricing model: Lite (10,000 MAU free), Essentials (10,000 MAU free), and Plus (no free tier). Each tier gates different feature sets (AWS Cognito Pricing).

The deep AWS integration is Cognito's signature strength. Native connections to IAM, API Gateway, Lambda, and ALB mean auth decisions can feed directly into infrastructure policies. Identity Pools are a unique capability: they issue temporary AWS credentials to authenticated users, granting scoped access to S3, DynamoDB, and other AWS resources without managing IAM users.

Native passkey and WebAuthn support landed in November 2024, available on Essentials+ (AWS Security Blog). Compliance inherits from AWS: SOC 1/2/3, ISO 27001, PCI DSS, FedRAMP, and HIPAA eligibility (Cognito Compliance). Managed Login UI (redirect-based) is available on Essentials+, and the Amplify UI Authenticator provides an embedded React component.

Considerations: Amplify documentation still references middleware.ts, with no proxy.ts guidance for Next.js 16. SAML and OIDC federation is free only for 50 MAU. Advanced security features (bot detection, adaptive auth, compromised credential detection) require the Plus plan at $0.02/MAU with no free tier. Passkeys and required MFA are mutually exclusive in the same user pool. User data is region-locked.

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:

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:

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:

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:

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

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

Clerk works with standalone React apps. No framework required.

npm install @clerk/react

Wrap the app with ClerkProvider using Vite's environment variable pattern:

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:

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

Clerk + Express: Secure auth in 5 minutes

Clerk's Express SDK provides middleware for backend APIs.

npm install @clerk/express

Attach clerkMiddleware globally, then protect specific routes with requireAuth:

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

Auth0 + Next.js 16

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

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

// 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():

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

Firebase Auth + React

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

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:

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

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.

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

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

WorkOS + Next.js 16

WorkOS AuthKit has a clean 4-step setup. 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").

// 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():

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

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

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:

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

Preventing account takeover

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 alone generated over 193 billion attempts globally in 2020 (Akamai, 2021). Password complexity rules haven't helped much: only 3% of compromised passwords actually met complexity requirements (Verizon DBIR, 2025). Phishing accounts for 15% of breaches, with an average cost of $4.76 million per incident (IBM Cost of a Data Breach, 2024).

Even 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). SIM swapping caused $25.98 million in reported US losses in 2024 (FBI IC3, 2024).

ATO prevention checklist

ProtectionHow it worksWhich APIs provide it
Phishing-resistant auth (passkeys)Asymmetric keys bound to originClerk, Auth0, WorkOS, Cognito (Essentials+)
MFA enforcementRequire second factor at loginClerk (Pro+), Auth0 (Essentials+), Supabase, WorkOS, Cognito (TOTP free)
Short-lived tokensReduce the window for compromised tokensClerk (60s), others configurable
Bot detectionBlock automated credential stuffingClerk (built-in), Auth0 (Attack Protection, Professional+), Cognito (Plus)
Account lockoutRate-limit failed login attemptsClerk (3 req/10s per IP, lockout at 100 attempts/1h), Auth0, Cognito
Breached password detectionCheck passwords against known breach databasesClerk, Auth0, WorkOS, Cognito (Plus)

The most effective single control remains MFA. Microsoft found that MFA blocks 99.9% of account compromise attacks (Microsoft, 2019). 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). 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 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 at $125 each, directory sync at $125 each, and $2,500/mo per million users 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. 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 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). 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). Over 3 billion passkeys are now in active use globally (FIDO Alliance, 2025), and 87% of surveyed US and UK enterprises with 500+ employees are deploying or planning to deploy them (FIDO Alliance, 2025).

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

Passkey support across auth APIs

ProviderNative passkeysNotes
ClerkPro+ planPro plan ($20/mo) and above
Auth0Via Universal Login
Firebase AuthThird-party extensions only
Supabase AuthNo native support
WorkOSIncluded on free tier
AWS CognitoEssentials+Not compatible with required MFA

Firebase and Supabase both lack native passkey support, which is a significant gap 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). 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), 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...ConsiderWhy
Fastest setup with embeddable UIClerkKeyless mode, prebuilt components, 4-step quickstart
Deepest React/Next.js integrationClerk15+ React hooks, Server Components, proxy.ts
Enterprise SSO with self-service adminWorkOSAdmin Portal, SCIM, $125/connection
Maximum free MAUWorkOS1M MAU free
Google Cloud/mobile ecosystemFirebase AuthNative Android/iOS SDKs, Firestore integration
Auth bundled with PostgreSQLSupabase AuthFull BaaS with Row Level Security
Most extensible auth pipelineAuth0Actions framework, 45+ SDKs
Strictest zero-trust token modelClerk60s TTL, automatic refresh, per-request validation
Native passkeys on free tierWorkOSPasskeys at no cost
Deep AWS ecosystem integrationAWS CognitoNative IAM, API Gateway, Lambda triggers
Cost efficiency at millions of usersAWS CognitoLite 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.

ScenarioRecommendedRunner-upWhy
Startup B2C (fast iteration, cost-sensitive)ClerkFirebase AuthClerk'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)WorkOSClerkWorkOS is purpose-built for enterprise readiness with SSO 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 CognitoClerk, Auth0Cognito 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 CognitoNative 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.

ProviderEstimated cost/moMetricNotes
WorkOS$0–$99MAU1M MAU free. Optional custom domain at $99/mo.
Supabase~$25MAUPro plan includes 100K MAU. Verify current limits at supabase.com/pricing.
Firebase~$125MAU50K free, then $0.0025/MAU on Blaze. No prebuilt auth UI. SMS MFA billed per message.
Clerk~$1,020MRUPro 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,350MAU10K free, then $0.015/MAU. Lite tier alternative: ~$495 but loses managed login UI and passkeys.
Auth0~$7,000 (est.)MAUEssentials $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). Supabase and Firebase offer neither built-in bot detection nor passkeys. WorkOS includes passkeys but charges separately for bot detection through Radar. 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.

ProviderUser data exportPassword hash exportImport with hashesLock-in level
ClerkCSV (dashboard), APIYes, self-service (16+ hash algorithms supported)YesLow
SupabaseDirect PostgreSQL accessYes, via database query (bcrypt)Yes (bcrypt, Argon2)Low
FirebaseCLI auth:export (JSON/CSV)Yes, modified scrypt with project keysYes (multiple algorithms)Moderate
Auth0API bulk export (NDJSON/CSV)Requires support ticketYes (10+ algorithms)Moderate-High
WorkOSAPI pagination onlyNot documentedYes (bcrypt)Moderate
AWS CognitoAPI/CSV (no passwords)Not possibleNo hash importHigh

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

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 the absence of native passkey support puts them at a growing disadvantage as the industry moves toward passwordless authentication.

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

Frequently asked questions