Tenancy
July 10, 2026 · View on GitHub
AuthProxy resolves a tenant ID string from each incoming request and stores it in the request context.
Downstream services receive the resolved tenant ID via the Tenant-ID header.
Tenant resolution strategies
Resolution strategies run in order until one strategy resolves a tenant.
Configure them under Cratis:AuthProxy:TenantResolutions:
{
"Cratis": {
"AuthProxy": {
"TenantResolutions": [
{ "Strategy": "Host" },
{ "Strategy": "Claim", "Options": { "ClaimType": "tid" } }
]
}
}
}
Available strategies
| Strategy | Description |
|---|---|
Host | Uses the request host and matches it against configured tenant Domains / SourceIdentifiers. |
Claim | Reads a claim value from the authenticated user and matches it against configured tenant SourceIdentifiers. |
Route | Extracts a source identifier from the request path by regex and matches it against configured tenant SourceIdentifiers. |
Specified | Resolves directly to a fixed tenant ID string from configuration. |
Default | Resolves directly to a fallback tenant ID string from configuration. |
SubHost | Resolves directly from subhost convention, for example acme.example.com -> acme. |
Selection | Resolves from the selected-tenant cookie set by the tenant-selection page flow. |
Claim strategy options
{
"Strategy": "Claim",
"Options": {
"ClaimType": "tid"
}
}
If ClaimType is omitted, AuthProxy falls back to reading X-MS-CLIENT-PRINCIPAL.
This is also how you resolve a tenant carried on an AuthProxy-issued client-credentials bearer token —
point ClaimType at cratis/tenant. See
Resolving a tenant from the verification response.
Route strategy options
{
"Strategy": "Route",
"Options": {
"Pattern": "^/tenant/(?<sourceIdentifier>[^/]+)"
}
}
The regex must expose a named group called sourceIdentifier.
Specified strategy options
{
"Strategy": "Specified",
"Options": {
"TenantId": "acme"
}
}
Default strategy options
{
"Strategy": "Default",
"Options": {
"TenantId": "lobby"
}
}
SubHost strategy options
{
"Strategy": "SubHost",
"Options": {
"ParentHost": "example.com",
"VerificationUrlTemplate": "https://platform.example.com/internal/tenants/{tenantId}"
}
}
| Property | Type | Description |
|---|---|---|
ParentHost | string | Parent host suffix used to extract the tenant ID from the request host. |
VerificationUrlTemplate | string | Optional strategy-specific verification URL template. Overrides the global TenantVerification.UrlTemplate for this strategy. |
How SubHost resolution works
The strategy strips the configured ParentHost suffix from the incoming request host to derive the tenant ID.
Given ParentHost: "example.com":
| Request host | Resolved tenant ID | Notes |
|---|---|---|
acme.example.com | acme | Single-segment subhost — resolved successfully. |
contoso.example.com | contoso | Single-segment subhost — resolved successfully. |
foo.bar.example.com | — | Multi-segment subhost rejected — not resolved. |
example.com | — | No subhost present — not resolved. |
other.com | — | Host does not end with .example.com — not resolved. |
The resolved subhost string becomes the tenant ID directly.
No Tenants dictionary lookup is performed — unlike Host, Claim, and Route strategies, SubHost does not match a source identifier against a pre-configured list.
This is intentional: SubHost is designed for environments where tenants are provisioned dynamically (for example SaaS platforms where each customer gets their own subdomain).
SubHost and authentication callbacks
For interactive authentication flows, AuthProxy stores SubHost strategy metadata in protected authentication state when it issues the provider challenge. On callback, AuthProxy restores this metadata and redirects to the tenant subhost.
This enables a shared callback endpoint (for example auth.cratis.studio) while returning the user to the original tenant host (for example nova.cratis.studio).
Because there is no registry lookup to prove the tenant exists, you should configure VerificationUrlTemplate to have AuthProxy call your back-end to confirm the tenant is valid before forwarding the request:
{
"Strategy": "SubHost",
"Options": {
"ParentHost": "example.com",
"VerificationUrlTemplate": "https://internal-api.example.com/tenants/{tenantId}"
}
}
AuthProxy replaces {tenantId} with the resolved subhost value and expects a 200 response.
Any other response causes the request to be rejected with tenant-not-found.html.
See Tenant verification for full response handling details.
Selection strategy options
{
"Strategy": "Selection",
"Options": {
"TenantsEndpoint": "https://platform.example.com/api/tenants/selectable"
}
}
| Property | Type | Description |
|---|---|---|
TenantsEndpoint | string | Absolute URL for the endpoint that returns selectable tenants for the current authenticated user. Expected response shape is an array of { "id": "...", "name": "..." } objects. |
When this strategy is configured and no .cratis-tenant cookie exists yet, AuthProxy calls
TenantsEndpoint, sets the .cratis-tenants cookie, and serves select-tenant.html.
The page links back to /.cratis/select-tenant?tenantId=<id>&returnUrl=<path>, and AuthProxy validates
the selected tenant against the endpoint response before writing the .cratis-tenant cookie.
Tenant registry
For Host, Claim, and Route, AuthProxy resolves a source identifier and then looks up the tenant ID in Cratis:AuthProxy:Tenants.
{
"Cratis": {
"AuthProxy": {
"Tenants": {
"acme": {
"Domains": ["acme.example.com"],
"SourceIdentifiers": ["acme", "tenant-acme"]
},
"contoso": {
"Domains": ["contoso.example.com"],
"SourceIdentifiers": ["contoso", "tenant-contoso"]
}
}
}
}
}
Lobby fallback
When no tenant can be resolved and the lobby is configured, AuthProxy redirects the user to the lobby frontend instead of returning 401 Unauthorized.
If no lobby is configured and TenantResolutions is non-empty, AuthProxy returns 401 Unauthorized.
When TenantResolutions is empty, the request proceeds without a tenant.
Tenant verification
After tenant resolution, AuthProxy can verify that the tenant exists before forwarding the request. This is optional.
Global verification configuration
{
"Cratis": {
"AuthProxy": {
"TenantVerification": {
"UrlTemplate": "https://platform.example.com/api/tenants/{tenantId}"
}
}
}
}
| Property | Type | Description |
|---|---|---|
UrlTemplate | string | URL template for tenant verification. Use {tenantId} placeholder. |
Response handling
200: tenant exists, request proceeds.404: tenant does not exist; AuthProxy servestenant-not-found.htmlwith404.- Any other status or network error: treated as tenant verification failure, and
tenant-not-found.htmlis served.
If a strategy provides a strategy-specific verification URL template (for example SubHost.Options.VerificationUrlTemplate), that template is used instead of the global TenantVerification.UrlTemplate.
Tenant not found page
When verification fails, AuthProxy serves tenant-not-found.html.
See Error pages to override this page.