
Authentication for Serverless and Edge Deployments
How does authentication work for serverless and edge deployments?
This is Part 1 of a three-part series on authentication for serverless and edge deployments. It covers why serverless environments require a different approach to authentication, core concepts like JWTs and stateless sessions, five architectural placement patterns, and a high-level overview of solution options.
Authentication for serverless and edge runtimes uses short-lived, stateless JWTs verified against a JWKS endpoint with local caching, because ephemeral compute cannot rely on long-lived session stores or in-memory caches. Managed providers handle key issuance, rotation, and edge-compatible SDKs; self-built options typically reach for jose with crypto.subtle.
Why Authentication Is Different at the Edge and in Serverless
The serverless and edge runtime model
Serverless compute is ephemeral: functions spin up per invocation, run briefly, and disappear. Examples include AWS Lambda, Vercel, Netlify, Azure, and Google Cloud Functions. The platform handles scaling, and you pay only for execution time.
"Edge" has two distinct meanings, and confusing them often breaks auth code.
- Architectural edge: compute placed geographically close to the user, but still running a familiar Node.js-style runtime. Next.js 16
proxy.tsis the canonical example — it sits in front of origin requests, but it runs on Node.js, not a V8 isolate (Next.jsproxy.tsAPI reference, Next.js 16 release blog). - Runtime edge: code that executes inside a V8 isolate or WASM runtime with Web Standards APIs only. Examples include Cloudflare Workers, Netlify Edge Functions (Deno), Deno Deploy, and Fastly Compute (WASM).
fs,net, and most of the Node standard library are either absent or gated behind compatibility flags (Cloudflare Workers Node.js compatibility).
A side-by-side comparison highlights the constraints:
Consequently, auth code for V8-isolates must be small, Web-Standards-native, and fast to start. Node.js serverless allows larger dependencies but pays a cold-start tax on instance turnover.
Why traditional session auth breaks down
A classical session model assumes a long-lived server with an in-memory cache backed by a nearby database. Three assumptions break here:
- Stateful session stores assume proximity. Each edge point-of-presence (PoP) is physically far from any central session DB, so every verification becomes a cross-region round-trip. That defeats the latency advantage of serving from the edge.
- In-memory caches do not persist. Serverless functions tear down after their invocation window. V8 isolates may be evicted at any time. Per-isolate caches exist, but a cold isolate starts with an empty one.
- Database round-trips per request are expensive at scale. A community benchmark measured warm execution at ~167ms on an edge runtime versus ~287ms on a serverless backend when the serverless tier hit a session database (ByteIota edge vs serverless). Treat this as illustrative of the shape, not a universal constant — methodologies vary.
Cookie-based sessions still work if the verification path is edge-compatible: a signed cookie verified with Web Crypto, or a JWT cookie parsed without Node-only libraries.
Cold starts, latency, and geographic distribution
Cold starts amplify auth latency because new instances pay for container spin-up, module evaluation, JWKS fetch, JWT parse, and database lookups. A sub-millisecond warm check can become a multi-hundred-millisecond delay.
- AWS describes Lambda cold starts as spanning "less than 100ms to well over 1 second" depending on runtime, package size, and VPC attachment (AWS Lambda cold start remediation).
- Independent benchmarks place optimized Node.js 22 arm64 Lambda cold starts around p50 ~294ms (Node.js 22 Lambda benchmarks).
- AWS SnapStart expanded to Python 3.12+ and .NET 8+ at re:Invent 2025 but is not yet available for Node.js (AWS Lambda SnapStart docs, AWS re:Invent 2025 Lambda recap).
Verify close to the user, authorize close to the data. A signed JWT answers "is this a valid token?" without a database. Authorization ("can the user perform this action?") usually requires state and runs near the data.
Runtime constraints that trip up auth libraries
Many "Node-first" auth libraries fail on V8-isolates because they depend on absent APIs.
jsonwebtokenuses Node'scryptomodule; CF Workers require Web Crypto (crypto.subtle) unlessnodejs_compatis enabled.bcryptships a native binary; it will not run in a V8 isolate.firebase-adminuses TCP sockets and Node.js-only APIs; it is not edge-compatible. Community librarynext-firebase-auth-edgefills the gap by reimplementing token verification with Web Crypto (next-firebase-auth-edge).pg(node-postgres) opens raw TCP connections; not available on CF Workers or Netlify Edge.- Most Passport strategies expect Express-shaped middleware and the Node
httpmodule; porting to Web Fetch handlers is non-trivial.
Bundle limits matter. Vercel Edge Runtime limits are 1-4MB gzipped (Vercel Edge Runtime docs). Cloudflare Workers allow 3-10MB compressed (Cloudflare Workers limits). Both favor small, Web-Standards-first SDKs.
Core Concepts for Serverless and Edge Authentication
Stateless authentication with JWTs
A JWT consists of three base64url-encoded segments: header.payload.signature. The header declares the algorithm and key ID (kid); the payload carries claims; the signature secures the token.
Stateless tokens fit ephemeral compute because verification only requires the issuer's public key—no server-side session record needed. Any function can verify any token without shared memory.
The tokens you usually encounter split into three roles:
- Access tokens: short-lived (15–60 minutes typical; Clerk session tokens are 60 seconds), sent on every API request, proving the caller's identity and scopes.
- Refresh tokens: long-lived, used to mint new access tokens without re-authentication.
- Session tokens: in some providers, the same short-lived JWT is used both as an access token and to drive session refresh (Clerk's
__sessioncookie is this shape).
The relevant specs are RFC 7519 (JWT), RFC 9068 (JWT Profile for OAuth 2.0 Access Tokens), and the OpenID Connect Core 1.0 spec for ID tokens.
JWT verification with JWKS
A JSON Web Key Set (JWKS) is a JSON document of the shape { "keys": [...] } where each entry is a public key with fields like kty (key type), kid (key ID), use, alg, and the key material itself (RFC 7517). Providers publish JWKS at a well-known URL — conventionally the issuer URL followed by the path /.well-known/jwks.json — discoverable via the OIDC Discovery Document.
Verifying a JWT against a JWKS follows this flow:
- Parse the header and read the
kid. - Fetch the JWKS (or read it from cache) and find the key with the matching
kid. - Verify the signature using the algorithm in the header, checked against your allow-list.
- Validate the required claims:
iss(issuer),aud(audience),exp(expiry),nbf(not-before),azp(authorized party, when applicable), and any custom claims the app cares about. - Allow a small clock skew — 5 to 30 seconds is typical (Curity JWT best practices).
Asymmetric algorithms (RS256, ES256, EdDSA) beat HS256 for multi-service verification since every service can hold the public key without sharing a secret. EdDSA and ES256 signatures are smaller, and EdDSA signing is faster. However, verification is the hot path for edge systems, where RSA-2048 remains comparable (Connect2id Nimbus benchmark, WorkOS HMAC vs RSA vs ECDSA).
A portable, Web-Crypto-native verifier looks like this:
import { createRemoteJWKSet, jwtVerify } from 'jose'
const JWKS = createRemoteJWKSet(new URL('https://your-issuer.example.com/.well-known/jwks.json'))
export async function verify(token: string) {
const { payload } = await jwtVerify(token, JWKS, {
issuer: 'https://your-issuer.example.com',
audience: 'your-app-audience',
algorithms: ['RS256', 'ES256'],
clockTolerance: '30s',
})
return payload
}That block runs unchanged in Node.js, Bun, Deno, and Cloudflare Workers. createRemoteJWKSet() caches the JWKS automatically and refreshes it on kid miss (jose GitHub).
JWKS caching at the edge
Fetching JWKS on every request adds a network round-trip. A community benchmark puts cold JWKS fetches at 15–25ms on Vercel Edge (SSOJet JWT at the edge). The fix is layered caching:
- Per-isolate in-memory (LRU). Free, fastest, warm-only.
jose'screateRemoteJWKSet()does this by default. - Shared KV. Cloudflare KV, Upstash, or Redis shared across isolates. Cloudflare's post-October-2025 rearchitecture documents hot-key KV reads
<1msand p99<5ms(Cloudflare KV performance). - Background refresh. Refresh the cache before the TTL expires to avoid a thundering herd at rollover.
Handling JWKS rotation requires publishing the new key early: pre-publish → wait for cache TTL → switch signing → retire old key (WorkOS JWKS guide, Zalando JWK rotation).
A NearForm benchmark on RS256 verification with an LRU cache showed a jump from 13,781 to 150,700 ops/sec (+993%) after caching public keys (NearForm JWT performance).
Session vs. token-based approaches
Cookie-based sessions work inside a single trust domain, especially under one Node.js deployment. They struggle at the edge when:
- The verification path cannot be edge-compatible (e.g., the session store is a Postgres DB reachable only over TCP).
- Requests span multiple origins or runtimes, so cookies do not automatically flow.
Short-lived JWTs are a better fit when:
- Multiple runtimes verify tokens from the same issuer (Node.js API + CF Workers edge + mobile clients).
- The latency budget is low enough that a DB round-trip per request is unacceptable.
A hybrid model uses a long-lived origin session and a short-lived JWT for the edge. Clerk implements this: __client is a long-lived cookie on Clerk's API, and __session is a 60-second JWT for the app (How Clerk works). The edge verifies the JWT, while the origin handles revocation.
Cookie flags checklist for any session cookie at the edge:
Secure— send only over HTTPS.SameSite=Laxas default (browsers apply this already).SameSite=NonerequiresSecureand is necessary for cross-origin flows.HttpOnlywhere feasible (prevents JS access; see Part 2 for Clerk's__sessionexception).Domain— scope to the narrowest domain that still works; widening to.example.comexpands XSS blast radius.Path— default/unless you specifically need a narrower path.
Machine-to-machine and service identity
Service-to-service calls need identity. The canonical pattern is the OAuth 2.0 client credentials flow: a service proves identity with a client ID and secret to receive a scoped, short-lived access token (RFC 6749, RFC 9700).
Design choices that matter more than the grant type:
- Asymmetric signed tokens (RS256 / ES256 / EdDSA) beat shared secrets for multi-service verification. Any service can verify with the public key.
- Short expirations — 15 minutes or less — reduce the blast radius of a leaked token.
- Scopes restrict what the token can do. The default should be the narrowest scope that works.
- Audit logs should record service-to-service calls with a stable machine identifier, not just "service X called service Y."
Not every provider implements client credentials as the OAuth 2.0 grant type. Clerk's M2M tokens are a distinct machine-auth product, not an RFC 6749 client credentials grant — covered in depth in Part 2.
Architectural Patterns for Serverless and Edge Authentication
These are authentication patterns ("who is this request?"). Authorization ("what can this request do?") runs afterward, usually near the data. These patterns optimize authentication so authorization can rely on trusted identity.
Pattern 1 — Authentication at the edge (middleware)
Verify at the nearest PoP before the request reaches origin functions.
client → edge middleware (verify JWT) → origin function → dataTypical placements:
- Next.js 16
proxy.ts(architectural edge, Node.js runtime). - Vercel Routing Middleware (runtime edge, still the Edge Runtime; not deprecated) (Vercel Routing Middleware docs).
- Cloudflare Workers with
routesor a front-door Worker. - Netlify Edge Functions wired as page routes.
This pattern spans both runtime families, but available APIs differ. Node-only primitives (jsonwebtoken) work in Next.js proxy.ts but break on Cloudflare Workers. Use Web Crypto + jose (or an edge-ready vendor SDK) for portability.
- Pros: single point of auth enforcement; unauthenticated requests are rejected before origin compute runs.
- Cons: misconfigurations can bypass auth entirely (CVE-2025-29927); must be paired with per-route verification for defense in depth.
- Use when: you have a single trust boundary and want to minimize origin load.
Pattern 2 — JWT verification inside each function
Every function verifies the token independently.
client → function A (verify) → data
client → function B (verify) → dataWorks for function-per-endpoint architectures (AWS Lambda, Netlify Functions, CF Workers with URL-based routing).
- Pros: no single point of failure; each function is self-contained; safer against middleware-bypass classes of bugs.
- Cons: duplicated verification logic; risk of drift between functions; each function pays its own cold-start tax.
- Use when: functions are heterogeneous or deployed separately; you cannot guarantee a common middleware layer.
Pattern 3 — Edge verification + origin session (hybrid)
Edge verifies a short-lived JWT; origin loads a rich session for Server Components or complex business logic.
client → edge (verify JWT) → origin (load session → render) → data- Pros: low-latency rejection at the edge plus a rich origin state model; instant revocation via the origin session record.
- Cons: more moving parts; session sync concerns between edge and origin.
- Use when: you need both geographic distribution and a rich server-side state that would not fit in claims.
Pattern 4 — Centralized auth gateway
A dedicated edge auth service issues and verifies tokens for other services—often a Backend-for-Frontend (BFF) or API gateway (Kong, Envoy).
client → gateway (verify, mint downstream token) → service A
→ service B- Pros: clean separation of concerns; a single place to update auth logic; easier to enforce consistent policies.
- Cons: additional hop; gateway itself has to scale; introduces a trust boundary that downstream services must respect.
- Use when: you have multiple backend services with mixed runtime targets and want a uniform identity story.
See microservices.io — Auth in Microservices Part 1 (BFF).
Pattern 5 — Signed request headers for service-to-service auth
Signed tokens passed between services via the Authorization: Bearer <token> header. The edge verifies and forwards identity downstream.
client → edge (verify) → forward Authorization header → service A → service B- Pros: simple; works with any HTTP service mesh; tokens are cacheable.
- Cons: token size on every request; a service mesh with sidecars is often a better fit at larger scale.
- Use when: internal microservices need zero-trust identity propagation without a full mesh.
See microservices.io — JWT Authorization, Frontegg authentication in microservices, and Oso microservices authorization patterns.
Choosing a pattern — decision factors
Four factors drive the choice:
- Runtime target — Node serverless, V8 isolate edge, or both.
- State auth needs to carry — a few claims (JWT-only is enough) vs. rich session data (need origin state).
- Latency budget per request — every network round-trip has to be justified.
- Client heterogeneity — single web app vs. web + mobile + internal services.
A short decision sketch:
- Primarily Node.js and a single Next.js 16 app → Pattern 1 with
proxy.ts, re-verifying in Server Components/Route Handlers. - Mixed runtimes, including Cloudflare Workers or Netlify Edge → Pattern 3 (hybrid) — edge verifies, origin keeps rich state.
- Microservices across many backends → Patterns 4 + 5 — a gateway verifies, downstream services consume forwarded identity.
- Function-per-endpoint on AWS Lambda with no consistent front door → Pattern 2, with a shared verifier library to avoid drift.
Solution Options for Serverless and Edge Authentication
For each option: what it is in one sentence, edge/V8-isolate compatibility, strengths, and limits. Deep Clerk coverage lives in Part 2 of this series.
Rolling your own JWT verification
Roll your own means wiring a library like jose against an identity provider you operate (or against your own issuer). The jose library is the de facto Web-Crypto-first choice — it runs in Node.js, Bun, Deno, Cloudflare Workers, and browsers with zero external dependencies.
- What you still have to build: sign-up/sign-in UI, password reset, MFA, organizations and RBAC, device management, key rotation, CVE tracking, refresh token rotation, JWKS caching, and secret rotation.
- Maintenance burden: every CVE in the auth space becomes your problem to track and mitigate.
- Use when: a very narrow API-only scope with no user UI and an existing IdP you trust.
AWS Cognito
Managed AWS service; JWKS-based verification works at the edge via the aws-jwt-verify library. The official pattern for CloudFront + Lambda@Edge is documented in the Authorization@Edge blog post.
- Strengths: AWS-native integrations; scale; official guidance for CloudFront + Lambda@Edge.
- Limits: UX customization is limited; token claim shape is opinionated; multi-cloud friction.
Auth0
Enterprise-grade IdP with JWKS-based verification.
- The Next.js SDK requires the
/edgesubpath for middleware;getSession()only works in Node.js runtime (auth0/nextjs-auth0). - Strengths: SSO, enterprise features, mature Actions/Rules ecosystem.
- Limits: per-MAU pricing. In November 2023, Auth0's pricing restructure raised B2C Essentials entry from $23/mo for 1,000 MAU to $35/mo for 500 MAU and raised the per-MAU overage from $0.023 to $0.07 (~3x) (Auth0 pricing change announcement, Auth0 pricing). Confirm current tiers against the live Auth0 pricing page before sizing cost.
Supabase Auth
JWT-based, easily verifiable at the edge. Supabase Auth migrated to asymmetric keys (RS256/ECC/Ed25519) in May 2025, so edge verification no longer requires sharing a symmetric secret (Supabase Auth JWTs docs).
- Strengths: bundled with Postgres + storage; simple flows;
getClaims()works in Deno Edge Functions. - Limits: tightly coupled to the Supabase backend; fewer enterprise features than Auth0 or Cognito.
Firebase Authentication
Token verification via Google's public keys.
firebase-adminis not edge-compatible; the community librarynext-firebase-auth-edgereimplements token creation/verification with Web Crypto for Next.js 16 and Node.js 24+ (next-firebase-auth-edge GitHub).- Strengths: mobile-first; generous free tier (Spark: 50K MAU free for email/social; confirm current tiers on the Firebase pricing page before sizing cost).
- Limits: Google-hosted identity model; custom claim flexibility requires the Admin SDK; phone auth is billed per SMS.
Clerk
Covered in depth in Part 2. Differentiators surfaced here:
- Edge-ready SDKs for Next.js, Cloudflare Workers, Hono, React Router (formerly Remix), Expo.
- Networkless verification via
jwtKey— no JWKS round-trip per request once the PEM public key is configured. - Managed JWKS and automatic key rotation; consumers never run their own rotation schedule.
- Built-in UI, organizations, MFA, passkeys, and M2M tokens.
Next steps
In this first part, we explored the constraints of edge and serverless runtimes and the architectural patterns used to place authentication in distributed systems. In Part 2, we will apply these concepts in a hands-on implementation, using Clerk to secure Next.js, Cloudflare Workers, and mobile runtimes.
Frequently Asked Questions
In this series
- Authentication for Serverless and Edge Deployments (you are here)
- Authentication for Serverless and Edge Deployments - Part 2
- Authentication for Serverless and Edge Deployments - Part 3