ZuploZuplo
LoginStart for Free
  • Documentation
  • API Reference
Introduction
Getting Started
    Develop using the Portal
      1 - Setup Your Gateway2 - Rate Limiting3 - API Key Auth4 - Deploy5 - Dynamic Rate LimitingMCP - Quick start
    Develop Locally
      1 - Setup Your Gateway2 - Rate Limiting3 - API Key Auth
Concepts
Development
Policies
Handlers
API Keys
    OverviewWhen to Use ThemBest PracticesAuthenticationManage in the PortalConsumers in a Specific BucketEnd-User AccessDeveloper APISelf-Serve IntegrationBucketsLeak DetectionService Limits
MCP Server
MCP Gateway
AI Gateway
Developer Portal
Monetization
Deploying & Source Control
Observability
Networking & Infrastructure
Account Management
Programming API
Build with AI
Zuplo CLI
Migration Guides
Platform LimitsSecuritySupportTrust & ComplianceChangelog
powered by Zudoku
API Keys

Authentication and Authorization

With the API Key Authentication Policy configured on your API routes you can build additional policies that run after the API Key Authentication policy to perform additional checks or authorization on the consumer.

Request User Object

After each successful authentication the policy will set the request.user object. The name of the API Key consumer is set to the request.user.sub property. Any metadata attached to the consumer is set to the request.user.data property. The interface of request.user is shown below.

Code
/** * The User object set by the API Key Authentication policy */ interface User { /** * The name of the API Key consumer */ sub: string; /** * The metadata attached to the API Key consumer */ data: any; }

So if you created a consumer with the following configuration:

Code
{ "name": "my-consumer", "metadata": { "companyId": 12345, "plan": "gold" } }

The request object would be the following:

Code
context.log.debug(request.user); // Outputs: // { // sub: "my-consumer", // data: { // companyId: 12345, // plan: "gold" // } // }

One question you might have is why is the request.user object not the same shape as the API Key Consumer object. for example why doesn't it has request.user.name and request.user.metadata properties.

The reason is because the request.user object is reused by many different kinds of authentication policies and they all conform to the same interface with sub and data.

Using Consumer Data in Code

It's possible to write additional policies that run after the API Key Authentication policy that perform further gating or authorization of the request based on the data set in the consumer.

For example, you could gate access to a feature by checking for the plan value stored in metadata (exposed via request.user.data.plan).

Code
async function (request: ZuploRequest, context: ZuploContext) { if (request.user?.data.plan !== "gold") { return new Response("You need to upgrade your plan", { status: 403 }); } return new Response("you have the gold plan!"); }

The metadata could also be used to route requests to dedicated customer services.

Code
async function (request: ZuploRequest, context: ZuploContext) { const { customerId } = request.user.data; return fetch(`https://${customerId}.customers.example.com/` }

The request.user object can be used in both handlers and policies

If you had a simple function handler as follows, it would return a request.user object to your route if the API Key is successfully authenticated:

Code
async function (request: ZuploRequest, context: ZuploContext) { // auto-serialize the user object and return it as JSON return request.user; }

Would send the following response.

Code
{ "sub": "my-consumer", "data": { "companyId": 12345, "plan": "gold" } }

Testing API Key Authentication

When running tests there are several ways to handle API Key authentication. The following strategies cover testing with API Key authentication both locally and in deployed environments.

Testing locally

When running API Key Authentication locally, if you link the project to a project, the same API Key bucket is shared by both your development (working copy) environment and local development.

Setting the API Key bucket name

Either locally or in CI/CD you can specify any API Key bucket on the API Key Authentication policy by setting the bucketName property. This allows using a consistent API Key bucket that is set up with consumers as required for testing. You can use the Zuplo Developer API to create and manage buckets, consumers, keys, and more.

Selectively disabling

Be extremely careful using this strategy. If configured incorrectly this could leave your API open to unauthorized access.

Another option is to disable authentication on endpoints for testing purposes. One way of doing this is to configure the API Key Authentication policy to allow unauthenticated requests through. This can be done by setting allowUnauthenticatedRequests to true.

In order to enforce authentication with this setting disabled, you can create a policy that comes after that selectively enforces auth based on some condition.

For example, an environment variable flag could be used to disable auth with the following policy.

Code
import { ZuploContext, ZuploRequest, environment, HttpProblems, } from "@zuplo/runtime"; export default async function enforceAuth( request: ZuploRequest, context: ZuploContext, ) { if (environment.DISABLE_AUTH === "AUTH_DISABLED") { return request; } if (!request.user) { return HttpProblems.unauthorized(request, context); } return request; }
Edit this page
Last modified on May 4, 2026
Best PracticesManage in the Portal
On this page
  • Request User Object
  • Using Consumer Data in Code
  • Testing API Key Authentication
    • Testing locally
    • Setting the API Key bucket name
    • Selectively disabling
TypeScript
JSON
TypeScript
TypeScript
TypeScript
TypeScript
JSON
TypeScript