Skip to content

Tenant identity

Every request carries its tenant context inside a signed JWT. Nothing in the request body or URL is trusted to say who the tenant is.

Tenant users live in a Cognito user pool with three custom attributes. Cognito copies them into the ID token:

Claim Example Used for
custom:tenantId 6e02dc17-5c4c-… Which tenant’s data the caller can reach
custom:userRole TenantAdmin or TenantUser What the caller may do
custom:tenantTier BASIC Which API key and usage plan apply
cognito:groups ["6e02dc17-5c4c-…"] Each tenant also has a Cognito group, used to list and check its users

Decoded claims from a pooled tenant admin:

{ "cognito:groups": ["tenant-a"], "custom:tenantTier": "BASIC", "cognito:username": "alice",
"custom:tenantId": "tenant-a", "custom:userRole": "TenantAdmin" }

The SaaS app’s dashboard shows these claims next to what the authorizer derived from them.

By default a Cognito app client lets a signed-in user change any of their own attributes with their access token, without going through your API. If custom:tenantId were writable, any user could move themselves into another tenant.

So the app client’s writeAttributes allows only email. Tenant attributes are only ever set by admin APIs (AdminCreateUser, AdminUpdateUserAttributes), which only the backend can call:

Terminal window
aws cognito-idp update-user-attributes --access-token "$ACCESS" \
--user-attributes Name=custom:tenantId,Value=tenant-b
# NotAuthorizedException: A client attempted to write unauthorized attribute
Who Created by Gets
The tenant’s first admin The provisioning job at onboarding TenantAdmin, the tenant’s id and tier, a temporary password by email
More tenant users A TenantAdmin in the SaaS app (POST /users) The role chosen. Tenant id and tier are inherited from the creating admin’s token, whatever the request body says
Role Can
TenantAdmin Everything in the tenant: products, orders and user management
TenantUser Products and orders, and read the user list
SystemAdmin Exists only in the control plane pool. The tenant API’s authorizer denies it, so system admins can’t read tenant data

Role rules live in one place, server/src/layers/auth_manager.py. See step 07 for every user management rule and how it’s enforced.