Skip to content

Operator SSO, SCIM and access levels ​

Who this is for: Platform operators and security owners
What you'll achieve: Sign different admins into /operator with SSO or a break-glass account, provision them from your directory, and keep a full audit trail

Everything on this page is configured in the console at Operator → Configuration. Environment variables are only needed to bootstrap a brand new deployment.

Ways to sign in ​

MethodSecond factorUse
OIDC SSOHandled by your IdP. Optional TOTP per operatorEveryday access for named humans, with roles from groups or assigned per person
Email and passwordTOTP always requiredBreak-glass when SSO is unavailable
Named API tokenNone (machine credential)CLI, automation, scripts
Bootstrap token (OPERATOR_API_TOKEN)NoneFirst-run setup only. Stops working once named access exists
Local bypass (OPERATOR_AUTH_DISABLED=1)NoneLocal and CI only. Ignored when NODE_ENV=production

With none of these available, /operator fails closed. It never falls open.

First-run setup ​

  1. Set OPERATOR_API_TOKEN on the host and sign in at /operator/login with Use bootstrap token.
  2. Give named humans a way in: either turn on SSO under Configuration → SSO, or go to Configuration → Admins and invite yourself as a platform_admin and follow the emailed link to set a password and enrol an authenticator.
  3. Go to Configuration → API tokens and create a named token for automation.
  4. Once named human access and a named token both exist, the bootstrap token stops being accepted. Remove it from the host.

If you need it back during an emergency, set OPERATOR_BOOTSTRAP_OVERRIDE=1. Every use is audited and emails your security address.

Configure SSO ​

  1. Register an OIDC app in Entra, Okta, or Google.
  2. Copy the redirect URI shown on Configuration → SSO into your IdP app. It is {APP_URL}/api/operator/auth/oidc/callback.
  3. Enter the issuer, client ID, and client secret. The secret is encrypted at rest with INTEGRATION_ENCRYPTION_KEY and never returned to the browser.
  4. Optionally restrict sign-in to specific email domains.
  5. Optionally map IdP groups to roles. Skip this if you would rather grant roles per person: see How roles are decided. Where someone matches several mapped groups, the highest role wins.
  6. Choose what happens for a first-time sign-in that matches no mapped group. Deny sign-in is the recommended default, and is safe even with no group mapping at all, because anyone you have already added keeps the role you gave them.
  7. Tick SSO is active and save.

Deployments already using the legacy OPERATOR_OIDC_* variables have them imported into this form once, automatically. After that the console is the only source of truth, so you can remove those secrets from the host.

You cannot switch SSO off if that would leave the shared bootstrap token as the only way in. Invite at least one admin with a password first, so you keep a break-glass account.

How roles are decided ​

Group mapping is optional. There are two ways to run this, and you can mix them.

Assign roles per person (simplest). Leave group mapping empty. On Configuration → Admins, add someone, pick their role, and choose Single sign-on as the sign-in method. They sign in with your identity provider and land on exactly that role. No password is created and MFA stays with your IdP. Because the account already exists, the default role can stay on Deny sign-in, so nobody who has not been added gets in.

Let the directory decide. Map at least one IdP group, or one SCIM group, to a role. Roles then follow group membership for everyone whose role you have not set by hand.

When a sign-in happens, the role is taken from the first of these that applies:

  1. A role you set on the Admins tab, marked Set here. Neither SSO nor SCIM overrides it.
  2. A local password account or a SCIM-provisioned record, for the same reason.
  3. The role the directory maps for that sign-in.
  4. Otherwise the role already on the account. A sign-in that maps nothing never silently relabels anyone.

An identity nobody has added, that the directory does not map either, is refused.

To hand a role back to the directory after setting it by hand, use Use directory next to that person. Their role is recalculated from group membership straight away and their sessions are revoked.

Directory provisioning (SCIM) ​

Configuration → SCIM gives you a SCIM 2.0 endpoint and bearer token for Entra, Okta, or Google.

  • Endpoint: {APP_URL}/api/operator/scim/v2
  • Supported resources: Users and Groups, including PATCH

Two rules keep provisioning safe:

  • Roles are never taken from the directory payload. New users arrive as readonly. Groups arrive with no role at all, and you choose what each one grants on the SCIM tab, or assign roles per person instead.
  • While any SCIM token is active, SSO only authenticates. It will not create accounts, so someone removed from your directory cannot sign back in and re-provision themselves.

Deprovisioning disables the account and revokes its sessions immediately, but keeps the record so the audit trail survives. SCIM refuses to deprovision the last active platform admin.

Once at least one group grants a role, group membership becomes authoritative for directory-managed operators. Removing someone from a mapped group lowers their role straight away, and losing every mapped group leaves them readonly. If that removes your last platform admin, the change still applies (stale privilege is the worse outcome) and your security address is emailed so you can restore access.

While no group grants a role, membership changes nothing at all, so syncing groups you have not mapped is harmless.

Local break-glass accounts and any operator whose role an admin set by hand are protected: SCIM can update their name and link their directory ID, but not change their role or take over their password.

Break-glass access ​

Invited operators set their own password and must enrol an authenticator app before they reach the console. Nobody, including you, can sign in with a password alone.

  • Passwords are at least 12 characters, hashed with bcrypt
  • Five failed attempts locks the account for 15 minutes
  • Ten backup codes are issued at enrolment, each usable once
  • Invite and reset links expire after 48 hours and work once

Reset someone's MFA or password from Configuration → Admins. Both revoke their existing sessions. A password reset keeps their existing authenticator: only an explicit MFA reset clears it and issues new backup codes.

MFA for SSO operators ​

SSO operators inherit whatever your identity provider enforces, so they are not challenged again by default. To require a SchoolRota authenticator as well, tick Require next to that person on the Admins tab. They will enrol on their next sign-in.

Roles ​

RoleProvisionFlags / billingImpersonateInboxAudit logConfiguration
platform_adminYesYesYesRead and replyYesYes
supportNoNoYesRead and replyYesNo
readonlyNoNo (view only)NoRead onlyYesNo

Named API tokens carry a role of their own and can be narrowed further to specific permissions, so automation does not have to run as an admin. The bootstrap token always acts as platform_admin.

Step-up re-authentication ​

Changing access settings and starting an impersonation session both need fresh proof of identity, even inside a live session. You are prompted inline and the action then continues on its own.

If you have an authenticator, you confirm with a code. If you sign in with SSO and have never set one up, you are sent back to your identity provider to sign in again instead, then returned to the page you were on. Either way the confirmation lasts 15 minutes. Machine tokens cannot perform these actions at all.

Sessions ​

Sessions last 8 hours, or 30 minutes idle. Configuration → Sessions lists every active session with its sign-in method, IP, and last activity, and lets you revoke any of them. Changing someone's role, status, password, or MFA revokes their sessions automatically.

Audit log ​

Every sign-in (including denied ones), logout, lockout, password set, MFA change, SSO and SCIM configuration change, token creation, provisioning action, flag change, and impersonation is written to audit_events with the acting operator. Browse at /operator/audit, filter to Access and sign-in only for a security review, and use Export CSV to take it away.

Optional network fence ​

Set OPERATOR_IP_ALLOWLIST to a comma-separated list of IPs or prefixes (for example 203.0.113.4,198.51.100.) to serve 404 for /operator and its API from anywhere else. The SCIM endpoint is exempt, since your identity provider calls it from its own addresses.

Optional REDIS_URL shares rate limits across machines and powers the BullMQ worker. Operator and tenant sessions stay in the database and cookies, not Redis. See Deployment.

What happens next ​

SchoolRota documentation. Every slot covered, every day.