Realms

A realm is one OIDC issuer — a self-contained login service with its own users, sign-in methods, assurance policy, and signing keys. This page covers creating realms and every setting on the realm settings page.

Creating a realm

In the admin console, open Realms and use the Create Realm card. There is a single field:

On submit, SemAuth automatically:

The realms list shows each realm's Name, Issuer ID (and custom domain if set), Status (Active / Disabled), and action links: Apps · Users · IDPs · Events · Settings · Discovery. The Discovery link opens the realm's /.well-known/openid-configuration.

Realm settings

Open Settings for a realm (/realms/{issuer_id}/settings). There are three cards.

Realm name

Change the display name. The value is trimmed and cannot be empty. Renaming never changes the issuer ID or any URL — it is purely cosmetic.

Authentication policy

The Minimum assurance dropdown sets the lowest authentication strength a sign-in must reach before this realm will issue tokens. Three options:

OptionMeaningSatisfied by
Any factor (AAL1)Any single factor is enough.Magic link, passkey, or federation.
Require MFA (AAL2)Multi-factor required.A passkey (today). One-time-code MFA arrives later.
Require phishing-resistant (AAL3)A phishing-resistant credential required.A passkey.

Guardrail. Because a passkey is currently the only factor stronger than AAL1, you can only select Require MFA or Require phishing-resistant when passkeys are enabled for the realm. Trying to set an unreachable floor is rejected ("This assurance level requires passkeys…") so you can't accidentally lock every user out at /authorize.

The full model — how each sign-in method maps to an assurance level, what the acr claim contains, and how step-up works — is on the Assurance levels page.

Enabling/disabling magic link & passkey. The two sign-in-method toggles (magic_link_enabled, passkey_enabled) are currently only changeable through the Admin API (PATCH /v1/realms/{issuer_id}), not from the settings page. New realms have both enabled.

Custom domain

Serve a realm at your own brand-aligned hostname (e.g. idp.acme.com) instead of the default {issuer_id}.semauth.app. Once active, the custom domain becomes the realm's iss, its JWKS URL, the link host in magic-link emails, the passkey binding (rp_id), and the federation callback target. The default host stays reachable as a fallback.

Set it before onboarding users. Changing the custom domain later invalidates every outstanding access token, refresh token, and passkey credential on the realm (the passkey binding is tied to the hostname). Configure it first, then onboard.

How to configure it

  1. At your DNS provider, add a CNAME record pointing your chosen hostname at the realm's canonical target shown in the UI: your-host CNAME {issuer_id}.semauth.app.
  2. Back in the settings card, enter the custom domain and click Verify CNAME & activate.

SemAuth resolves the CNAME chain (up to 5 hops) using public DNS resolvers only (Cloudflare 1.1.1.1 and Google 8.8.8.8) and activates the domain only if it points at the canonical target. TLS certificates are issued automatically on first request.

Rules and common errors:

To remove it, click Clear custom domain. The realm reverts to its canonical host; tokens issued for the custom hostname become invalid.

flowchart LR DNS["idp.acme.com<br/>CNAME"] --> Target["{issuer_id}.semauth.app"] Verify["Verify CNAME & activate"] -->|"resolves via 1.1.1.1 / 8.8.8.8"| Check{"Points at<br/>canonical target?"} Check -->|yes| Active["Custom domain active<br/>TLS auto-issued"] Check -->|no| Fail["Verification failed"]

Signing keys

Each realm has its own RSA 2048 key pair used to sign ID tokens and JWT access tokens with RS256. The active public key is published at the realm's /jwks.json, which is how resource servers and your apps verify tokens statelessly. Private keys are stored encrypted at rest. Keys are generated automatically at realm creation, and Kovilo rotates them without downtime: a new key is published alongside the current one before it is used, and the old one stays published until the tokens it signed have expired. Always select the verification key by the token's kid.

Limits

An organization can hold up to 10 realms (a disabled realm still counts; an erased one does not), of which up to 3 may carry a custom domain, changed at most 5 times per hour. Within a realm: up to 100 clients, 50 applications and 10 external identity providers. An organization can declare up to 20 APIs. Going over a limit is refused with an explanatory error.

Disabling a realm

A disabled realm stops resolving — its hostname no longer serves OIDC endpoints: no sign-in, no tokens. Re-enabling restores it. Use the Status switch in the realm's Settings, or the Admin API (PATCH /v1/realms/{issuer_id} with enabled).

Erasing a realm

When you retire a realm for good, erase it to destroy the personal data of every one of its users. Disable the realm first; the Erase realm panel then appears in its Settings. Type the realm's issuer ID to confirm.

This cannot be undone. To erase a single person rather than a whole realm, see Erasing personal data.