Designing Tenant-Aware Authentication with API Gateway and Lambda
Multi-tenant applications must authenticate the user while also determining which tenant—customer, account, or organization—the user belongs to. That tenant context must remain trustworthy as the request moves through API Gateway, Lambda functions, and the data layer.
This article presents a practical pattern for carrying tenant identity through a serverless AWS API without trusting tenant identifiers supplied by the client.

Authentication Is Only the First Check
Authentication answers: Who is making this request?
A multi-tenant application must also answer:
- Which tenant does this user belong to?
- Is the user active within that tenant?
- Which operations can the user perform?
- Which tenant data may the request access?
The tenant identifier should come from a trusted identity token, not from a query parameter, request header, or URL supplied by the client.
A common implementation uses a JSON Web Token, or JWT. A JWT is a signed token containing claims such as the user identifier, token issuer, expiration time, roles, and tenant identifier.
A token might contain claims similar to these:
{
"sub": "user-789",
"tenantId": "clinic-123",
"roles": ["scheduler"],
"scope": "appointments:read appointments:write"
}
The token proves that the identity provider issued those claims. It does not automatically guarantee that every Lambda function will use them correctly.
A Practical Authentication Pattern
1. Issue the tenant identifier as a trusted claim
Use Amazon Cognito, an external identity provider, or a custom authentication service to issue the JWT.
Include a stable tenant identifier such as tenantId. Keep claims small and limited to information needed for request authorization. Avoid placing large permission documents, customer configuration, or frequently changing attributes inside the token.
2. Validate the token at API Gateway
Configure an API Gateway JWT authorizer when the identity provider supports standard JWT validation.
API Gateway can validate important token properties, including:
- The digital signature
- The token issuer
- The intended audience
- The expiration time
- Required scopes
Requests with invalid or expired tokens should be rejected before the backend Lambda function runs.
A Lambda authorizer may be more appropriate when authorization requires custom logic, legacy tokens, multiple identity providers, or an immediate tenant-status check.
3. Pass verified identity context downstream
After validation, API Gateway exposes the verified claims through the request context. The backend Lambda function should read tenantId, sub, roles, and scopes from that context.
It should not accept an alternative tenant identifier from the client.
const claims = event.requestContext.authorizer.jwt.claims;
const tenantId = claims.tenantId;
const userId = claims.sub;
if (!tenantId || !userId) {
return {
statusCode: 403,
body: JSON.stringify({ message: "Missing tenant context" }),
};
}
Centralize this extraction and validation in a shared function so that every endpoint applies the same rules.
4. Enforce authorization in the Lambda function
Token validation proves that the claims are authentic. The backend must still decide whether the operation is allowed.
For example, a user with appointments:read may retrieve appointments but should not modify them. A tenant administrator may manage staff but should not access platform-level administrative operations.
Perform coarse checks with token scopes or roles. Perform resource-specific checks in the application when authorization depends on current data, ownership, subscription state, or business rules.
5. Scope every data operation by tenant
Tenant isolation must continue into the database.
For DynamoDB, include the tenant identifier in the partition key or primary access pattern:
PK = TENANT#clinic-123
SK = APPOINTMENT#2026-08-04#appointment-456
For Aurora or another relational database, include tenant_id in queries and indexes:
SELECT *
FROM appointments
WHERE tenant_id = :tenantId
AND appointment_id = :appointmentId;
Do not retrieve a record by its global identifier and check the tenant afterward. Make the tenant condition part of the database operation itself.

Example: Appointment Scheduling SaaS
Consider a scheduling platform serving independent medical clinics.
A user signs in through Cognito and receives a token containing:
{
"sub": "user-789",
"tenantId": "clinic-123",
"scope": "appointments:read"
}
The user calls:
GET /appointments
Authorization: Bearer eyJ...
API Gateway validates the token and passes its claims to the appointments Lambda function. The function extracts clinic-123 from the verified claims and queries only records whose partition key begins with TENANT#clinic-123.
Even if the client sends this header:
X-Tenant-Id: clinic-999
the application ignores it. Tenant identity comes exclusively from the validated token context.
Implementation Checklist
- Add a stable tenant identifier to the user’s trusted identity record.
- Include that identifier as a signed token claim.
- Validate token issuer, audience, signature, expiration, and scopes.
- Reject requests without verified user and tenant identifiers.
- Read tenant context from API Gateway, never from client input.
- Apply role, scope, and resource-level authorization separately.
- Include the tenant identifier in every database access pattern.
- Log
tenantId,userId, request ID, route, and authorization outcome. - Test attempts to access another tenant’s identifiers.
- Return generic authorization errors without exposing tenant details.
Trade-Offs and Common Mistakes
JWT authorizer versus Lambda authorizer
A JWT authorizer is simpler and avoids executing custom authorization code for each request. Its limitation is that it validates token claims but does not automatically check whether a tenant was suspended moments after the token was issued.
A Lambda authorizer can perform additional checks, but it adds execution cost, latency, code, and another failure point. Authorizer caching reduces repeated work but can delay the enforcement of permission or tenant-status changes.
Treating the tenant claim as complete authorization
A valid tenantId only establishes tenant context. It does not prove that the user can perform every operation within that tenant.
Keep authentication, tenant resolution, permission checks, and data isolation as distinct controls.
Allowing unscoped data access
The most dangerous implementation mistake is a repository or query function that accepts only a record identifier:
getAppointment(appointmentId);
Prefer interfaces that require tenant context:
getAppointment(tenantId, appointmentId);
This makes unsafe access harder to introduce during routine development.
Conclusion
A reliable multi-tenant authentication design carries a verified tenant identifier from the identity provider through API Gateway and into every Lambda and database operation. API Gateway should reject invalid identities early, while backend functions remain responsible for authorization and tenant-scoped data access.
As a next step, review one critical API route from token validation to database query and identify every point where tenant context could be missing, replaced, or ignored.