Authentication
XTM One supports local accounts and single sign-on (SSO) via OIDC and SAML 2.0. All authentication behavior is configured from Settings → Security in the UI (admin-only). No environment variables are needed beyond the initial admin bootstrap.
The first account is always an admin
Whichever way the very first account is created — local registration, admin bootstrap, or the first SSO login — it is granted the admin role. Every later account defaults to the standard user role unless promoted.
Local accounts
By default, users authenticate with email and password.
The bootstrap admin account (ADMIN_EMAIL / ADMIN_PASSWORD) is created or reset from environment variables on every startup. If you change that password in the UI, the next restart resets it back to the .env value — keep them in sync.
Local authentication is governed by two toggles under Settings → Security:
- Local Authentication (default: enabled) — allows email/password sign-in. When disabled, local login and local registration are blocked and users must sign in through SSO (an "SSO-only" deployment). The very first account can always be created, even while this is off, so you are never locked out.
- Local Registration (default: disabled) — lets users self-register with email and password. When disabled, only admins can create accounts.
Restricting registration by email domain
When local registration is enabled, you can restrict it with Allowed email domains (comma-separated; empty means all domains are allowed). Registration from a domain outside the list is refused.
Domain allow-list applies to self-registration only
The allowed-domains list gates local self-registration. It does not filter SSO logins — control SSO access from your identity provider instead.
Admin-managed accounts
Admins manage accounts from the Users page (admin-only). Two mechanisms are available:
- Create a user directly — add an account and set its role from
Users→New user. - Send an invitation link — from
Users→Invite user, generate a single-use link bound to a specific email address and role. The invitee sets their own password by opening the link.
How invitation links work
Each invitation is bound to one email address, carries the role you choose, and can be used once. The link expires after a configurable window (default 7 days, set with INVITATION_EXPIRE_HOURS, capped at 30 days). Invitations bypass the Local Registration and Allowed-domains settings, so you can onboard a specific person without opening self-registration to everyone.
Two-factor authentication (TOTP)
Any user can add a second factor: a six-digit code from an authenticator app, entered after the first factor on every sign-in. It is enabled per user from Profile → Two-factor — there is nothing to configure at deployment level and no environment variable to set.
The factor is XTM One's own, and it applies whichever way the user signs in — local password, OIDC, or SAML. It is not delegated to the identity provider. That is deliberate: many deployments federate to a provider whose MFA policy they do not control (a customer tenant, a partner IdP, a legacy directory), and being able to enforce a second factor at the application boundary is the point. Where the IdP already enforces one, the two simply stack, and users are free not to enroll.
Any RFC 6238 authenticator app works, including Google Authenticator, Microsoft Authenticator, KeePassXC, 1Password, Bitwarden, Aegis and FreeOTP. XTM One uses the parameters those apps assume by default (SHA-1, 6 digits, 30-second period), so enrollment is a QR scan with no per-app tuning. The QR is rendered by your own server, so enrollment works on air-gapped deployments — no third-party service is contacted.
Enrollment is two-phase on purpose: scanning the QR is not enough, the user must type a code back to prove the app was enrolled correctly. Until they do, nothing changes about how they sign in — closing the tab halfway cannot lock anyone out.
Recovery codes
Enabling two-factor issues 10 single-use recovery codes, displayed once and never again. Each one signs the user in once, in place of the app, if they lose their device. Users can generate a fresh set (invalidating the old one) from their profile, which requires a current code from the app.
If a user loses their device and their codes
An admin can clear the enrollment from the user's detail page (Users → select the user → Two-factor → Reset two-factor). The account then signs in with its password alone until the user enrolls a new device. The action is audited.
Administrators never see the secret
The TOTP secret is encrypted at rest with the platform SECRET_KEY, and recovery codes are stored hashed. Neither can be read back by anyone, including admins — resetting is the only recovery path, and it deliberately forces a fresh enrollment.
Rotating SECRET_KEY invalidates every enrollment
The encryption key for stored secrets is derived from SECRET_KEY. Changing it leaves existing enrollments unreadable: affected users can still sign in with a recovery code, and admins can reset their enrollment, but their authenticator app stops working.
How it works with SSO
After a successful OIDC or SAML assertion, an enrolled user is returned to the sign-in page for the code step instead of being signed in. No session token is issued until the code is verified, so the extra factor cannot be skipped by replaying the callback URL.
An SSO-only user disables their factor with a code alone — they have no XTM One password to confirm with. Accounts that do hold a local password are asked for both.
Until the code is accepted, the audit log records the event as "Authenticated via OIDC, awaiting second factor", not as a completed login. The completed login is recorded separately, and names the provider it came through.
Local authentication can stay disabled
Two-factor does not require enabling local passwords. An SSO-only deployment (Local Authentication off) can enroll every user in an authenticator app and keep the IdP as the sole first factor.
Scope
Two-factor applies to interactive sign-in — local, OIDC, and SAML alike.
API keys (fcp-…), channel bots and cross-platform tokens from OpenCTI/OpenAEV are unaffected: they are machine credentials with nowhere to prompt for a code.
Enabling, disabling, recovery-code use and failed verifications are all recorded in the audit log, under the mfa entity type. Each entry is attributed to the flow it came from via its action — login, enable, disable, or update (regenerating recovery codes) — so the audit filters separate a failed code at sign-in from a mistyped one while turning the factor off.
That separation is what makes the log useful for detection: a run of failed second factors with action login and no matching successful login is the signal that someone holds a password but not the device.
OIDC (OpenID Connect)
Use OIDC to integrate with identity providers such as Microsoft Entra ID (Azure AD), Okta, Google Workspace, or Keycloak. OIDC uses the Authorization Code flow.
What you need
- A registered OAuth 2.0 / OIDC application in your IdP
- A Client ID and Client Secret
- Either a Discovery URL (the
.well-known/openid-configurationendpoint — recommended) or the individual authorization, token, and userinfo endpoint URLs - The Redirect URI registered in your IdP:
{BASE_URL}/api/auth/oidc/callback
Configuration
Configure OIDC from Settings → Security. The fields are:
| Field | Notes |
|---|---|
| Display Name | Label shown on the sign-in button (e.g. "Microsoft") |
| Discovery URL | Recommended — auto-resolves the other endpoints |
| Client ID / Client Secret | From your IdP application; stored encrypted |
| Scopes | Default openid email profile |
| Manual endpoint URLs | Optional — set the authorization, token, and userinfo URLs directly if you are not using discovery |
The exact Redirect URI to register in your IdP is displayed in the configuration panel.
User provisioning
Users are created automatically on first login. XTM One matches the identity in this order:
- By the SSO subject (
sub) of a previously linked account. - Otherwise by email — if a local (or other) account with the same email exists, it is linked to the OIDC identity.
- Otherwise a new account is created.
The IdP must return an email claim; a login with no email is rejected. The user's name and job title are updated from the IdP claims on each login.
On-the-fly user creation
An On-the-fly user creation toggle (default: on) controls step 3 above. When
disabled, only pre-provisioned accounts can sign in via OIDC: an identity
that matches no existing account (by SSO subject or email) is refused before any
account is created, and the person is sent back to the login page with a
readable message (?sso_error=not_provisioned). Steps 1 and 2 are unaffected —
existing accounts keep signing in and linking by email as before.
Use this when account creation must go through an explicit administrative step (invitations, an IdP-side approval flow, or manual provisioning) rather than being implied by IdP access.
Automatically admin users
An optional Automatically admin users toggle (default: off) grants the admin role to anyone who signs in via OIDC. It is promote-only — it never demotes existing admins, and turning it off later does not strip roles already granted. OIDC does not support group mapping; use SAML if you need group-driven roles.
When sign-in fails
Changed behaviour. A failed or abandoned OIDC sign-in used to leave the
browser on a raw JSON error — an API error body, or a validation error about a
missing code parameter. It now returns to the login page with a readable
message and a marker in the URL:
?sso_error= |
When | What the person sees |
|---|---|---|
cancelled |
They declined the consent screen at the identity provider | Sign-in was cancelled; try again when ready |
not_authorised |
The XTM Hub entitlement gate declined them (see below) | Their account is not authorised for this instance |
not_provisioned |
On-the-fly user creation is disabled and no account matches their identity | No account exists; ask an administrator to create one first |
failed |
Any other provider error, or a malformed callback | Single sign-on failed; contact your administrator |
This applies to every OIDC deployment, whether or not the XTM Hub gate below is configured. Nothing about who can sign in changes — only how a failure is presented.
The message is deliberately vague in every case. The specifics — the provider's error code and description, or the reason an account was not entitled — are written to the server log, because someone who has not signed in should not be able to read your identity provider's configuration back out of an error page. The marker is scrubbed from the URL once shown, so a reload does not re-accuse someone who has since signed in successfully.
XTM Hub entitlement gate
If this instance is federated through XTM Hub, the Hub can decide which XTM
One instances each person may reach, and whether they are an administrator on
each one. Set XTM_HUB_ADMIN_GROUP to switch it on — see
Configuration. The gate identifies
this instance by its PLATFORM_ID, which must be set too.
It is off unless configured, and when on it takes precedence over Automatically admin users above: that toggle only promotes, while the Hub also demotes.
Two consequences are worth knowing before you enable it:
- A person the Hub has not entitled here is refused before an account is created, so no dormant accounts accumulate — but an existing user who loses their entitlement is also refused on their next sign-in.
- Administrator is removed when the Hub stops asserting it. The
ADMIN_EMAILaccount is never demoted for exactly this reason, and local sign-in remains available as a recovery path if the gate is misconfigured. - Every role change the Hub causes is written to the audit log, in the same shape as one made from the admin UI, so "who became an administrator, and when" has one answer regardless of who decided it.
SAML 2.0
Use SAML for enterprise identity providers that require SAML assertions. XTM One acts as the Service Provider in an SP-initiated flow.
What you need
- Your IdP metadata URL (or XML)
- The ACS (Assertion Consumer Service) URL:
{BASE_URL}/api/auth/saml/acs - The Entity ID / SP metadata URL:
{BASE_URL}/api/auth/saml/metadata
Configuration
Configure SAML from Settings → Security. The fields are:
| Field | Notes |
|---|---|
| Display Name | Label shown on the sign-in button (e.g. "Corporate SSO") |
| IdP Metadata URL | Your identity provider's federation metadata |
| SP Entity ID | Optional — leave empty to use the metadata URL |
User provisioning
Users are created automatically on first login, matched by email in the same order as OIDC (SSO subject, then email linking, then create).
SAML has the same On-the-fly user creation toggle as OIDC (default: on).
When disabled, an assertion that matches no existing account is refused before
any account is created and the browser returns to the login page with
?sso_error=not_provisioned.
Automatically admin users
Like OIDC, SAML has an Automatically admin users toggle (default: off) that grants the admin role on SAML login. It is promote-only.
Group mapping
SAML supports automatic group mapping from IdP group claims (enabled by default). XTM One reads groups from the common claim URIs used by Microsoft Entra ID, Okta, ADFS, and generic SAML providers.
- Groups from the assertion are synced on every login: the user is added to matching XTM One groups and removed from groups no longer present in the assertion.
- With Auto-create groups enabled (default), groups in the assertion that do not yet exist in XTM One are created automatically.
Both settings live under the SAML section of Settings → Security. See Groups for how groups control entity sharing.
Terms of Use acceptance
After signing in — with any method above — users are shown a blocking Terms of Use screen and cannot reach any part of the UI until they explicitly accept. Acceptance is recorded server-side (account, version, timestamp) and the gate is enforced on the API too, so it cannot be skipped by calling the backend directly.
The gate only applies where Agentic AI is licensed. Community Edition deployments never see it.
| Variable | Default | Purpose |
|---|---|---|
TERMS_OF_USE_ENABLED |
true |
Set to false to disable the gate entirely — for deployments that handle Terms of Use acceptance outside the product. |
TERMS_OF_USE_VERSION |
2026-07-14 |
Identifier of the published document. An empty value disables the gate. |
TERMS_OF_USE_URL |
https://filigran.io/ai-terms/ |
Where the document is published. The screen links to it; it is not embedded. |
Changing the version re-prompts everyone
Users are gated whenever their recorded acceptance does not match TERMS_OF_USE_VERSION. Bumping it is how you roll out a revised document: every user is asked to accept again on their next request, including those who had already accepted the previous version. There is no admin UI toggle — a revision is a deliberate configuration change.
Admins can review acceptance on the Users page: each account shows whether it has accepted the current version, and its detail page shows the accepted version and date.
Access provisioning summary
| Method | How users are created | Where |
|---|---|---|
| Admin creates a user | Admin adds an account and sets its role | Users → New user |
| Admin invitation link | Single-use, email-bound link the invitee opens to set a password | Users → Invite user |
| Self-registration | Users register with email and password (optionally domain-restricted) | Sign-in page, when Local Registration is enabled |
| SSO auto-provisioning | Users are created on first OIDC or SAML login, linked by email | On first SSO login |
Account hardening summary
| Hardening | What it adds | Where |
|---|---|---|
| Two-factor (TOTP) | A six-digit code from an authenticator app, required after the password | Profile → Two-factor (per user) |
| Admin reset of two-factor | Clears an enrollment for a user who lost their device | Users → user detail → Reset two-factor |
Next step
Once authentication is configured, proceed to Administration to manage users, groups, and permissions.