Accounts and isolation
Each account has its own realm. This page says what that separates, what stays shared, and what the decision costs.
An account is the record RealmSSO keeps for one of your customers. It is sometimes called a tenant. Each account has its own realm. A realm is a separate sign-in space with its own users, clients, identity-provider links and signing keys.
Between realms, tokens, sessions and identity-provider links do not cross. Below the realm, accounts share the server, the sign-in service, the databases, the key ring and the machine. Isolation is logical.
Why each account has its own realm
RealmSSO first put every account in one shared realm. It told accounts apart by name only. A name is not a boundary. In a test on 2026-07-30, a user signed in through one account's identity provider. The user got a valid access token and refresh token for another account's client. That account had no connections of its own.
The token carried nothing that named the account. The receiving application could not detect the mismatch. The login page of that account also listed the sign-in buttons of every other account's identity provider. That disclosed the customer list.
No account lives in a shared realm today. The old shared realm was deleted in production on 2026-09-14.
The decision: a realm for each account
RealmSSO creates the realm in the same request that creates the account. The realm name is kr- followed by the account id. The client ID for your application is org-<slug>, so it contains the account's short name (the slug).
The URLs that your customers register do not contain the customer's name. The ACS URL and the redirect URI contain the realm name and a connection ID. The accounts API returns the account's realm name in the field keycloakRealm.
What the realm separates
After the change, we ran attacks between two realms on a test deployment of the sign-in service. The table shows three of them. Each one failed. This page names no sign-in service version, because we have not re-measured for the current release.
| Attack between realms | Result |
|---|---|
| Sign-in through another realm’s identity-provider alias | HTTP 400. Identity-provider aliases belong to one realm. |
Authorize request with another realm’s client ID (org-<other>) | HTTP 400 "Client not found". Client IDs belong to one realm. |
| SSO cookie replay | The cookie is limited to its own realm’s path. The browser does not send it to another realm. |
These results are for attacks between realms. They say nothing about two accounts in one realm.
The token's issuer is the account's own realm URL. Your application must check the issuer and the audience. A standard OIDC library checks the issuer by default.
What the realm does not do
Everything below the realm is shared
All hosted accounts share one RealmSSO server, one sign-in service, both databases, one key ring and one machine. A realm separates accounts in software. It does not give an account its own server.
One account for each realm is a design rule. Nothing in the database forces it. A scan runs at start-up and every five minutes. If one realm holds two accounts, the scan writes a row to the platform record. The scan finds a breach after the fact. A person must then act.
Inside a realm, the server checks that an account owns an object before it reads or changes it. If the server cannot prove ownership, it refuses. SCIM writes are refused when the account does not own its realm. This is application code, not a database rule.
Deleting an account
Deleting an account deletes its realm. The realm teardown runs after the database commit, and it can fail. RealmSSO records a failure. See Backup, recovery and deletion.
Email domains are verified with DNS
An account claims an email domain. The claim counts only after a DNS TXT record verifies it. RealmSSO checks the record at _realmsso-challenge.<domain>. You can start a claim in the dashboard. The routes below also accept a dashboard session token. They do not accept an API key.
/api/v1/accounts/:id/domain/claimRecords the domain and issues a TXT challenge.
/api/v1/accounts/:id/domain/verifyChecks the challenge against real DNS and records the result. Setting domain with PATCH /api/v1/accounts/:id takes the same path. A new domain never inherits the verification of an earlier one.
Two accounts cannot hold the same domain. The check ignores letter case. A collision returns 409.
GET /api/v1/sso/resolve routes a login by email domain (see Connections). It needs no credential. It matches only a verified domain. It gives one answer for an unknown domain, an unverified domain and a domain with no active connection. For a match, it returns the account's id, name and slug, and its oldest active, provisioned connection.
What is still open
Building now. The design would let one account hold several verified email domains, each routed to a different identity provider. An account has one email domain today.
Building now. The edge would bind each per-customer hostname to its realm and refuse an unknown hostname. Per-customer hostnames are built and switched off in production. Until the binding exists, a per-customer hostname would be a nicer address, not a boundary.
Realm capacity was never load-tested. We give no limit on the number of accounts. See Limits and capacity.
RealmSSO has no production customers yet, so no customer has used this model in production. See Security overview.