Lesson 9 / 32

Identity Tokens and Tenant Claims

Read tenant information from verified token claims, know which claims to trust, and avoid client-controlled tenant headers.

By · 15 years in web development

Trust signed claims, not client input

After login, an identity provider usually issues a signed token, often a JWT. Its claims can include the user id (sub), the audience, an expiry, and tenant information such as the active tenant_id or a list of memberships with roles. Because the token is signed, your API can trust claims after verifying the signature, issuer, audience and expiry. Contrast that with an X-Tenant-Id header sent by the browser: anyone can edit it, so on its own it proves nothing. If you accept such a header to let a user switch tenants, treat it only as a request and check it against the verified memberships. Keep tokens small: embedding hundreds of memberships bloats every request, so some systems put only the active tenant in the token and issue a new token when the user switches tenants. Role changes are another reason for short token lifetimes, since a revoked admin keeps their power until the token expires.

A token payload with an active tenant

An illustrative decoded JWT payload. Claim names other than the registered ones (iss, sub, aud, exp) are your own design.

{
  "iss": "https://auth.example-saas.com",
  "sub": "user_8f21",
  "aud": "api.example-saas.com",
  "exp": 1767609000,
  "tenant_id": "acme",
  "role": "admin"
}

Gateways may set headers, browsers may not

A common pattern is an API gateway that verifies the token and then forwards a trusted X-Tenant-Id header to internal services. That is safe only if services are unreachable except through the gateway and the gateway strips any incoming copy of that header from clients.

Quick check: Why is a browser-supplied `X-Tenant-Id` header not enough to authorise access to a tenant?

  • Headers are limited to 8 characters
  • Browsers cannot send custom headers
  • The client controls it, so it must be checked against verified memberships
  • HTTP/2 drops custom headers
Answer

The client controls it, so it must be checked against verified memberships — Anything the client sends can be forged. Only signed, verified claims or a server-side membership lookup can prove the user belongs to the tenant.