Zuplo

API Key + JWT Multi-Auth

Accept either an API key or a JWT on the same route with a composite inbound policy.

API Key + JWT Multi-Auth

This example shows how to accept either an API key or a JWT on the same route, using a composite inbound policy. Requests succeed if either credential is valid; unauthenticated requests get a 401.

This pattern is useful for:

  • Mixed clients: Server-to-server traffic uses long-lived API keys, while end-user traffic uses short-lived JWTs from your IdP
  • Migration paths: Onboard JWT-based auth without breaking existing API key consumers
  • Single endpoint, multiple identities: Resolve request.user from whichever credential the caller presented

Prerequisites

  • A Zuplo account. You can sign up for free.
  • An OIDC-compatible IdP (Auth0, Clerk, Cognito, Okta, Supabase, etc.) that issues JWTs and exposes a JWKS endpoint.

Deploy this example to Zuplo

Click the Deploy to Zuplo button anywhere on this page to create a new project in your Zuplo account with this example pre-configured.

After deploy, set the JWT environment variables in your Zuplo project (Settings → Environment Variables):

VariableDescription
JWT_ISSUERYour IdP’s issuer URL (e.g. https://your-tenant.auth0.com/)
JWT_AUDIENCEExpected aud claim for tokens issued to this API
JWT_JWKS_URLJWKS endpoint your IdP publishes (e.g. https://your-tenant.auth0.com/.well-known/jwks.json)

API keys are managed in the Zuplo portal under API Key Service — create a consumer and key there.

How It Works

A single composite policy (dual-auth) runs three inbound policies in order:

  1. api-key-auth — validates Authorization: Bearer <api-key>. Sets request.user if the key matches a Zuplo-managed consumer. allowUnauthenticatedRequests: true lets the request continue to the next policy if no key is present.
  2. jwt-auth — validates a JWT against the configured issuer, audience, and JWKS URL. Sets request.user from the token claims. Also runs in allowUnauthenticatedRequests: true mode.
  3. require-auth — custom code policy that checks request.user.sub. If neither prior policy authenticated the caller, it returns a 401.

Whichever credential matched first populates request.user, so handlers don’t need to know which auth method was used.

Project Structure

plaintext
├── config/
│   ├── routes.oas.json         # Single /me route protected by dual-auth
│   └── policies.json           # api-key-auth, jwt-auth, require-auth, dual-auth composite
├── modules/
│   ├── forward-slash-me.ts     # Returns the authenticated user's sub + data
│   └── require-auth.ts         # Gate policy: 401 if request.user.sub missing
└── zuplo.jsonc

API Endpoints

MethodPathDescription
GET/meReturns sub and data from request.user

Testing

Replace YOUR_GATEWAY_URL with your deployed gateway’s URL.

1. No credentials → 401

Terminalbash
curl -i https://YOUR_GATEWAY_URL/me

2. With an API key

Terminalbash
curl -i -H "Authorization: Bearer YOUR_API_KEY" \
  https://YOUR_GATEWAY_URL/me

3. With a JWT

Terminalbash
curl -i -H "Authorization: Bearer YOUR_JWT" \
  https://YOUR_GATEWAY_URL/me

Both authenticated calls return:

JSONjson
{
  "message": "Authenticated",
  "sub": "...",
  "data": { ... }
}

Extending This Example

  • Role-based access: Inspect request.user.data in require-auth.ts and reject requests missing required scopes or roles
  • Per-route auth: Apply api-key-auth only to machine routes and dual-auth to user-facing routes
  • Additional providers: Add a second open-id-jwt-auth-inbound policy for a second IdP and include it in the composite

Troubleshooting

ErrorCauseFix
401 with valid JWTJWT_ISSUER / JWT_AUDIENCE mismatchDecode the token at jwt.io and confirm iss and aud match your env vars exactly (trailing slash matters)
401 with valid API keyKey not provisionedCreate the consumer + key in the Zuplo portal’s API Key Service
500 on first requestJWT_JWKS_URL unreachableVerify the JWKS URL returns JSON in a browser

Learn More

Related Examples

Explore more examples in this category

Starter

Basic API Gateway

Authentication

Protect your backend API with authentication, rate limiting, and request validation.

Backend for Frontend (BFF) Auth

Authentication

Secure your web app by handling authentication server-side without exposing tokens to the browser.

Canary Routing

Routing

Gradually roll out new backend versions by routing a percentage of traffic to canary before full deployment.

Starter

Dev Portal with API Keys

Authentication

Let developers sign up and create their own API keys through a self-serve portal.