SenseCrypt Docs
Concepts

Multi-tenancy & issuers

Each SenseCrypt tenant is its own OIDC issuer and SAML IdP on its own hostname, with its own signing keys and a hard isolation boundary. Resolve every endpoint from discovery.

SenseCrypt is multi-tenant, and the unit of tenancy is the tenant. Every tenant is its own OIDC issuer and SAML IdP, served on its own hostname and signed with its own keys. This is the most important structural fact to keep in mind when you integrate: you do not integrate against "SenseCrypt", you integrate against one specific tenant's issuer.

One tenant, one issuer, one hostname

A tenant has a globally unique slug, and its issuer lives at a hostname derived from that slug:

https://{slug}.{your-domain}

That URL is the tenant's OIDC issuer value and the base for all of its protocol endpoints — discovery, JWKS, authorize, token, userinfo, SAML metadata, SAML SSO, and SCIM. The tenant is resolved from the request host: the hostname selects which tenant a protocol request belongs to. A tenant's SAML entityID is {issuer}/v1/idp/saml, a distinct URL under the same host so the OIDC and SAML protocol identities never collide.

The flow below traces a single protocol request from its host header through tenant and issuer resolution to the tenant's own JWKS — the same path discovery, authorize, token, and jwks.json all take, and where the per-tenant key boundary is enforced:

yes — slug host orverified custom domain forged / absent yes no no yes verify by iss + kid Protocol requestHost / X-Forwarded-Host X-Forwarded-Host namesa host this deployment serves? Use forwarded host Fall back to trustedHost header — #162 slug parses from host?slug_from_host() SELECT tenantWHERE slug = … AND not deleted Verified custom-domain lookuptenant_for_custom_host() tenant resolved? 404 — unknown issuer host issuer = scheme://bare_hostmirrors the validated host jwks(tenant.id):WHERE tenant_id = tenant.id Serves only THIS tenant'sES256 key(s) + any in the grace window Hard isolation:tenant A's token never verifiesagainst tenant B's JWKS

Because the issuer is host-derived, the live issuer string in a discovery document is echoed from the actual host the request arrived on. Always resolve endpoints from that tenant's discovery document rather than hard-coding paths — this is the pattern the Add Login quickstart follows:

curl https://{slug}.{your-domain}/.well-known/openid-configuration

Read authorization_endpoint, token_endpoint, jwks_uri, and the rest from that document. See OIDC & OAuth 2.0 for the full discovery contract, or the discovery API reference.

Slug rules

The slug is a valid lowercase DNS label (1–63 characters, lowercase alphanumerics and internal hyphens). It is:

  • Globally unique across all accounts — no two live tenants anywhere share a slug.
  • Immutable after creation. The slug is the public subdomain baked into the issuer URL; renaming it would break every relying party that trusts that issuer. (A tenant's human-readable display name can be renamed freely; the slug cannot.)
  • Not a reserved label. Common infrastructure names (www, admin, api, and similar) are reserved and rejected.

Per-tenant signing keys

There is no deployment-wide signing key. Each tenant owns its own key material, created automatically when the tenant is created:

ProtocolKeyKey id (kid)
OIDC (ID / access / M2M tokens)ES256 (NIST P-256)RFC 7638 JWK thumbprint of the public key
SAML (assertions)RSA-2048 with a self-signed X.509 certificateSHA-256 fingerprint of the certificate

Both keys are rotatable per tenant, with an overlap (grace) window so already-issued tokens keep verifying while relying parties pick up the new key:

  • OIDC uses a one-shot rotation: a fresh key is inserted and immediately becomes the signer; the outgoing key keeps verifying until its grace window lapses. During that window the JWKS publishes more than one key, so an RP must select the verification key by the token's kid and tolerate multiple keys being present. See Tokens & sessions.
  • SAML uses make-before-break (stage → activate): a new certificate is published in metadata before it starts signing, so an SP that re-fetches metadata trusts assertions from either side of the switch. During the overlap, the metadata document contains multiple <KeyDescriptor use="signing"> certificates. See SAML.

The practical rule for OIDC verification: fetch the JWKS, select the key whose kid matches the token header, and cache with the understanding that the key set can grow during a rotation. Do not pin a single key.

Key private material is held either in software custody (sealed at rest in an AES-256-GCM envelope) or, per tenant, in AWS KMS as a non-exportable key the service signs through but can never export. This is a custody detail — it does not change how you verify tokens (you always verify against the public JWKS / certificate). See Security.

Isolation

Tenants are a hard isolation boundary — nothing crosses between them:

  • A token minted in one tenant can never validate against another tenant's JWKS, because each tenant signs with its own key. Trusting the iss claim and verifying against the JWKS at that issuer is therefore sufficient to know exactly which tenant issued a token.
  • A client (OAuth app) provisioned in one tenant cannot be used on another tenant's issuer host — a client_id presented on the "wrong" issuer is rejected.
  • Every user, application, group, resource server, scope, and signing key belongs to exactly one tenant.
  • Sealed face references are bound to a single tenant id and fail closed if replayed against another tenant.

Accounts vs. tenants

There are two levels, and the distinction matters:

  • An account is the console owner — the entity that registered and manages tenants, billing, and administrators.
  • A tenant is the isolation boundary.

An account can hold multiple tenants, but the account is not the isolation boundary — the tenant is. Two tenants under the same account are as isolated from each other as two tenants under different accounts: separate issuers, separate keys, separate directories. The same person enrolls separately per tenant (their face reference in tenant A is meaningless in tenant B).

Every account is given one auto-created default tenant that cannot be deleted; you can create additional tenants as needed.

What this means for your integration

  • Configure your app against one tenant's issuer URL and resolve its endpoints from that tenant's discovery document.
  • If you operate across multiple tenants, treat each one as a fully separate IdP: a separate issuer, a separate JWKS, and separate client registrations. Do not assume a client or token from one tenant is usable in another.
  • Trust the iss claim. It identifies exactly which tenant issued a token, and — combined with per-tenant keys — is a reliable tenant discriminator. Verify each token against the JWKS of its own iss.
  • Do not hard-code hostnames or paths beyond the tenant's issuer base. Endpoint paths come from discovery; keys come from the JWKS.
  • OIDC & OAuth 2.0 and SAML — the per-tenant protocol surfaces and their discovery/metadata documents.
  • Tokens & sessions — the iss, sub, kid, and key-rotation details for verification.
  • Security — the key custody model and isolation enforcement.

On this page