Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Tenants and tenant settings

A tenant is an isolated identity realm with its own issuer (https://id.example.com/t/acme), users, clients, keys and settings. This page covers creating and managing tenants and every key of the tenant settings document. For what a tenant is, see Tenants and issuers.

The master tenant

Every deployment has a master tenant, created by the first migration. Its users are global administrators (see Administrator access). It cannot be disabled or deleted. Keep ordinary users and applications in their own tenants; master is for the people who run the deployment.

Creating a tenant

Creating tenants needs ridm:tenants:create, which only a global owner holds.

WhereHow
ConsoleTenants (/console/tenants/) → New tenant; you land on the new tenant’s settings
CLIridm tenant create acme --name "Acme Corp"
APIPOST /admin/tenants
POST /admin/tenants
Authorization: Bearer rpat_…
Content-Type: application/json

{ "slug": "acme", "display_name": "Acme Corp", "settings": { "registration": { "enabled": true } } }
  • slug is 1–63 lowercase letters, digits or hyphens, not starting or ending with a hyphen. It is part of every URL of the tenant (/t/acme/…) and cannot change.
  • display_name is 1–255 characters; it is shown on the login pages and in the console.
  • settings is optional: anything left out takes the defaults below.
  • data_region is optional: on a deployment with data regions, the region whose database keeps the tenant’s data (GET /admin/regions lists them; absent or home: the home database). It is fixed at creation; ridm-api move-tenant moves a tenant later.

A new tenant is seeded with the standard scopes (openid, profile, email, phone, address, offline_access, features), the urn:ridm:admin and urn:ridm:account resource servers, the six built-in admin roles, and the two built-in console clients (ridm-admin-console, ridm-account-console). Its first signing key is made on first use. It has no users: invite or create an administrator for it next (see Users, invitations and bulk import).

Managing a tenant

TaskConsoleAPIPermission
List tenantsTenantsGET /admin/tenants?cursor=&limit=ridm:tenants:read; tenant administrators see only their own
Read a tenantSettingsGET /admin/tenants/{slug}ridm:tenants:read
Rename, disable, change settingsSettingsPATCH /admin/tenants/{slug}ridm:tenants:write
DeleteSettings → delete zone (global owners)DELETE /admin/tenants/{slug}ridm:tenants:delete, global
Export / import configurationExport & importGET …/export, POST …/importridm:tenants:export / ridm:tenants:import

PATCH takes display_name, status (active or disabled) and settings. The settings are a JSON merge patch (RFC 7396) over the stored document: send only what changes, and null resets a key to its default. A field name the settings document does not have is refused with 400 (“unknown settings fields: …”) rather than silently dropped, because it is almost certainly a typo; that includes the removed registration.captcha. Tagged settings switch variant cleanly: {"mfa": {"mode": "optional"}} replaces a required_for_roles policy without clearing roles by hand.

curl -s -X PATCH "https://id.example.com/admin/tenants/acme" \
  -H "Authorization: Bearer $RIDM_TOKEN" -H "Content-Type: application/json" \
  -d '{"settings": {"password": {"min_length": 14}, "mfa": {"mode": "required_for_admins"}}}'

A disabled tenant refuses every sign-in and token request, and its tokens stop working at the admin API; administrators can still read it through the admin API and re-enable it. Deleting a tenant permanently removes it and everything in it, with no soft-delete period.

The console’s Settings page (/console/settings/) shows every setting on one page and saves as you go: changes are joined into one merge patch sent once typing pauses. The header shows “Unsaved changes”, “Saving…”, “Saved”, or the API’s reason for refusing, after which the stored settings are reloaded.

To keep settings in version control, export the tenant with ridm tenant export acme -o acme.json and apply edits with ridm tenant import; see Configuration as code.

The settings document

Every key has a default, and a stored document written by an older version keeps loading. The top-level keys:

KeyGovernsDetails
passwordPassword rules and historybelow
sessionBrowser sessions, trusted devices, default token lifetimesbelow
mfaWhen a second factor is demandedMFA policy
mfa_methodsWhich second factors users may enrolMFA policy
riskScoring sign-ins, and what an unusual one costsAdaptive authentication
impersonationWhether administrators may sign in as users, and for how longImpersonation
authWhich first-factor sign-in methods are offeredbelow
registrationSelf-registrationbelow
lockoutBrute-force lockoutbelow
captchaWhen a CAPTCHA is demandedRate limits, IP rules and CAPTCHA
rate_limitsRequest ceilings on the OAuth and sign-in endpointsRate limits, IP rules and CAPTCHA
notificationsSecurity notices to usersbelow
accountWhat users may do in the account consolebelow
localeLanguages of the login pagesbelow
brandingLook of the login pagesbelow
keysSigning key algorithm and rotationbelow
discoveryWebFinger issuer discoverybelow
dcrDynamic client registration: disabled, open or initial_access_tokenRegistering clients
auditAudit log retentionbelow
custom_domainThe tenant’s own issuer hostCustom domains
featuresFeature flags applications readFeature flags

password

KeyDefaultMeaning
min_length12Minimum characters
max_length128Maximum characters
require_uppercasefalseNeeds an uppercase letter
require_lowercasefalseNeeds a lowercase letter
require_digitfalseNeeds a digit
require_symbolfalseNeeds a character that is neither alphanumeric nor whitespace
history5A new password must differ from this many previous ones, the current one included (0 off)
max_age_daysnullDays until a password expires and must be changed (null never)
check_breachedfalseRefuse passwords found in breach corpora

The policy applies to users’ own changes, registration, invitations, admin-set passwords (unless skip_policy is sent) and imported plaintext passwords. Imported hashes are not checked, since the plaintext is unknown.

session

KeyDefaultMeaning
idle_timeout_secs1800 (30 min)A browser session ends after this long without use
absolute_timeout_secs43200 (12 h)A session ends this long after sign-in, whatever its use
max_concurrent0Live sessions per user; the oldest is signed out first, with back-channel logout to its clients (0 unlimited)
remember_device_days30How long a “remember this device” cookie skips the second factor
access_token_ttl_secs300Default access token lifetime
refresh_token_ttl_secs2592000 (30 days)Default refresh token lifetime
id_token_ttl_secs300Default ID token lifetime

A client may set its own token lifetimes, which take precedence over these defaults (see Registering clients).

The session timeouts bound refresh tokens too. A refresh token issued without the offline_access scope belongs to the browser session it came from: it stops working once that session ends, whether by sign-out, idle_timeout_secs or absolute_timeout_secs, and each refresh counts as activity that extends the idle window. A refresh token issued with offline_access outlives the session’s timeouts and lasts its own refresh_token_ttl_secs, though an explicit sign-out still revokes it. Clients that need long-lived access without a browser, device-flow clients among them, should request offline_access. See Tokens.

auth

First-factor methods shown on the login page.

KeyDefaultMeaning
passwordtrueUsername or email and password
magic_linkfalseA sign-in link by email
email_otpfalseA one-time code by email
sms_otpfalseA one-time code by text message (needs an SMS gateway, see Email, SMS and templates)
passkeyfalsePasswordless sign-in with a passkey; also enables passkeys as a second factor

registration

KeyDefaultMeaning
enabledfalseShow “create an account” and accept self-registration
require_email_verificationtrueNew accounts stay pending until the emailed link is followed
require_termsfalseRegistrants must accept the terms
terms_urlnullTerms of service link, shown to registrants and on the terms step
privacy_urlnullPrivacy policy link
allowed_email_domains[]Only these email domains may register (empty: any)

Whether registration demands a CAPTCHA is captcha.on_registration (see Rate limits, IP rules and CAPTCHA); in the console it sits under Settings → Passwords & lockout. The earlier registration.captcha key is gone: a migration carried any true value over to captcha.on_registration, a PATCH naming it is refused, and a tenant import ignores it.

The fields a registrant fills in beyond username, email and password come from the tenant’s profile schema (see Users).

lockout

KeyDefaultMeaning
max_failures10Consecutive failed sign-ins before the account is locked temporarily (0 off)
lock_minutes15How long that lock lasts
ip_max_failures100Failures from one address within the window before it is throttled (0 off)
ip_window_minutes15That window

An administrator clears a lock with POST /admin/tenants/{slug}/users/{user}/unlock or the Unlock button on the user page.

notifications

Security notices sent to the user by email, or by text message when they have no email address. All default to true.

KeySent when
new_deviceSomeone signs in from a device not seen before
password_changedThe password changes
mfa_changedA second factor is added or removed
email_changedThe email address changes (sent to the previous address)

account

What users may do to their own account in the account console. These keys are not on the console’s Settings page; change them with PATCH /admin/tenants/{slug} or a tenant import.

KeyDefaultMeaning
self_deletiontrueUsers may delete their own account
deletion_retention_days30Days a deleted account (by the user or an administrator) is kept before the daily purge removes it
personal_tokenstrueUsers may mint personal access tokens
personal_token_max_days365Longest, and default, personal access token lifetime (0 no limit)

locale

KeyDefaultMeaning
default"en"Language used when neither the request nor the user names a supported one
supported["en"]BCP 47 tags the login pages and messages may use

Tags are normalised on save (pt_BR becomes pt-BR); a malformed tag, or a default outside supported, is refused. An empty supported list becomes [default]. The language of a page or message is negotiated from the OIDC ui_locales parameter, then the user’s stored locale, then default; a tag matches exactly or by language (de-CH matches a supported de). Right-to-left languages (Arabic, Hebrew, Persian, Urdu and others) set dir="rtl". The flow state carries the result as locale, dir (ltr or rtl) and the selectable locales; every email or SMS the flow sends is rendered in that locale, and a self-registered user is stored with it.

The bundled UI ships English strings only (ui/src/i18n/en.json). An added bundle falls back key by key (de-CH, then de, then English), so a partial translation still renders; plurals follow Intl.PluralRules, and the stylesheets use logical properties so right-to-left languages mirror the layout. Message templates can be overridden per locale; see Email, SMS and templates.

branding

How the tenant looks: the sign-in pages, the account console, and the admin console while it administers this tenant. Settings → Appearance in the console edits it with a live preview.

KeyDefaultMeaning
logo_urlnullLogo on the login pages
favicon_urlnullBrowser tab icon
primary_colornullThe theme’s primary colour: buttons, navigation, links, focus. A hex colour (#rrggbb) drives a full set of light and dark shades; any other CSS colour literal sets the plain accent only
contrast_colornullThe theme’s second colour, for what must read as different from the primary: selected rows, the tenant mark. Hex; derived from the primary when unset
background_colornullPage background behind the card (a CSS colour literal)
skin"signal-v2"The visual skin: signal-v2 (default), signal, vault or registry
mode"system"Light/dark policy: system follows each person’s device, light and dark force one
allow_theme_switchtrueShow a light/dark switch so people can override mode on their device
support_urlnull“Need help?” link
headlinenullOne line for the sign-in page’s brand panel (at most 120 characters). With the Signal v2 skin the page splits into the panel and the form once this is set; the other skins keep the single card
taglinenullA sentence under the headline (at most 240 characters)
custom_cssnullStylesheet applied to the login pages after the theme
links[]Extra footer links, each {"label": "…", "url": "…"}

Colours are checked on write: a value that is not a CSS colour literal is refused, and contrast_color must be hex. The console shows the contrast ratios the two colours achieve on the page and on buttons in both modes, and flags anything under 4.5:1, but it does not refuse a brand colour over it.

Branding, the display name, locales and the offered sign-in methods are published at GET /t/{slug}/branding, a public, cacheable document every end-user page loads. The console’s appearance editor (Settings → Appearance) frames the real login page in preview mode and applies each draft change as you type. Images are loaded from their URLs over https; the pages’ Content-Security-Policy allows images and fonts from any https origin, so host them anywhere.

keys

KeyDefaultMeaning
default_alg"RS256"Algorithm for keys made by rotation: RS256, RS384, RS512, ES256, EdDSA
rsa_bits"B2048"RSA key size: B2048, B3072, B4096
rotation_interval_days90Rotate the active key automatically after this many days (0 only on demand)
retire_overlap_hours24How long a retired key stays published so tokens it signed still verify

See Rotating keys.

discovery

KeyDefaultMeaning
email_domains[]WebFinger acct:user@<domain> lookups for these domains resolve to this tenant’s issuer

audit

KeyDefaultMeaning
retention_days365Days to keep audit rows (0 forever)

See Webhooks and the audit log.

features

A map of flag name to flag, default {}: each with enabled, an optional description and optional per-organization values. rIDM itself doesn’t act on them; applications read them through the features scope or GET /t/{slug}/features. Edit them on the console’s Feature flags page. See Feature flags.

{ "features": { "beta_dashboard": { "enabled": true, "organizations": { "globex": false } } } }

custom_domain

A hostname such as login.acme.example (optionally with a port), lower-cased on save. It must not be the deployment’s own host and must not be used by another tenant. Default null, meaning the issuer is {PUBLIC_URL}/t/{slug}. See Custom domains.

Settings kept outside the document

Some tenant configuration holds secrets and so lives behind its own endpoints, stored encrypted and never returned:

ConfigurationEndpointsPage
CAPTCHA provider (site key, secret)GET/PUT/DELETE /admin/tenants/{slug}/captchaRate limits, IP rules and CAPTCHA
Email and SMS providers, templates/admin/tenants/{slug}/messaging/…Email, SMS and templates
User profile schemaGET/PUT /admin/tenants/{slug}/profile-schemaUsers
IP allow and deny rules/admin/tenants/{slug}/ip-rulesRate limits, IP rules and CAPTCHA

Limits

Some configuration is read whole (by the console, the tenant document and token issuance), so each tenant has a ceiling on it. Creating one more past the ceiling is a 400:

CollectionPer tenant
Groups10,000
Roles5,000
Scopes1,000
Resource servers1,000
IP rules1,000
Webhooks100
SCIM tokens (live)100
Trusted certificate authorities (mutual TLS)50
Feature flags200

Users, clients, organizations and the audit log have no ceiling: their lists are paginated.