# Identity Tokens and Tenant Claims — Multi-Tenant SaaS Architecture

Source: https://www.skillbyai.com/en/multi-tenant-saas/mt-tokens-and-claims

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

## 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.

```json
{
  "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.

**Quiz:** 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
- [x] 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.
