Single Sign-On & SCIM
Open Settings → SSO in the Control Center to connect your identity provider (IdP). The tab has three cards:
| Card | What it does |
|---|---|
| SAML | Sign in to the Control Center with a SAML 2.0 IdP (Okta, Entra ID, OneLogin, JumpCloud, …) |
| OIDC | Sign in with an OpenID Connect provider (Google Workspace, Okta OIDC, Keycloak, Auth0, …) |
| SCIM provisioning | Let Okta or Azure AD (Entra ID) create, update and remove Control Center users and map groups to roles |
Each card shows its status (connected, off or error), the IdP host or your sign-in domain, and how many people use it.
Sign-in buttons on the login page
The login page shows Continue with Google, Continue with GitHub and Continue with SAML SSO / SSO buttons:
- Your workspace’s SSO buttons appear once someone types an email address from a domain your workspace has verified (see Verify your sign-in domain). The page never shows your workspace name. It shows only a generic button.
- Google and GitHub buttons appear for everyone when the operator of the Control Center enabled them (see Operator setup). They sign in existing accounts whose email matches the verified email of the Google or GitHub account.
After the IdP round trip you land where you were going (for example back on the fleet page). If your account uses two-factor authentication, you still enter your TOTP code.
Sign-in is refused, with a message on the login page, when:
| Message code | Meaning |
|---|---|
no_account | No Control Center account for that email (and it could not be created, see below) |
not_provisioned | Your workspace uses SCIM, and the IdP has not provisioned you yet |
account_disabled | You were deprovisioned in this workspace (or in every workspace, for Google / GitHub) |
email_unverified | The IdP did not confirm your email address |
account_conflict | The account could not be added to this workspace |
tenant_suspended | The workspace is suspended |
idp_misconfigured / invalid_assertion / invalid_id_token | The connector settings or the IdP response are wrong. Ask your admin |
People in several workspaces
One Quazzar account can belong to several workspaces:
- Signing in through your workspace’s SAML or OIDC connector opens your workspace only. That session cannot switch to the person’s other workspaces; they sign in with their password, Google or GitHub for that.
- Google and GitHub sign-in lands in the workspace that verified the email’s domain, otherwise in the workspace the person used last.
- When SCIM or just-in-time sign-in adds someone who already has an account in another workspace, this workspace is added to their account. Their password is not changed, and SCIM here cannot change their email or name while the account also belongs to another workspace.
- Deactivating someone through SCIM only removes their access to your workspace.
Just-in-time accounts
When someone signs in through your workspace’s SAML or OIDC connector with an email in a verified domain and has no account yet, the Control Center creates one with the Viewer role. It does not do this when SCIM provisioning is on: then SCIM is the only way accounts are created.
Verify your sign-in domain
Owners and admins add the email domain in Settings → SSO → Sign-in domains:
-
Enter your domain, for example
acme.com. Public mail domains (gmail.com, outlook.com, …) are not allowed. -
Publish the TXT record shown:
_quazzar-sso.acme.com. TXT "quazzar-sso-verification=<token>" -
Click Verify. Once DNS has propagated, the domain is marked verified. If the record isn’t visible yet the domain shows record not found and keeps the record on screen; if another workspace already verified the domain it shows claimed elsewhere.
Only one workspace can verify a domain. Until you verify it, the domain does not route sign-ins to your workspace.
Connect a SAML IdP
-
In Settings → SSO → SAML, click Configure and choose the SAML connector.
-
In your IdP, create a SAML 2.0 app with:
IdP field Value Single sign-on URL / ACS URL https://<control-center>/api/auth/sso/saml/<connector-id>/acsAudience / SP Entity ID https://<control-center>/api/auth/sso/saml/<connector-id>/metadataName ID format EmailAddress (recommended) Attribute email(ormail)the user’s email The SP metadata XML is also available at the Entity ID URL above.
-
Paste the IdP’s settings into the connector configuration:
Key Description idp_metadata_urlIdP metadata URL (Okta: View SAML setup instructions → IdP metadata) idp_metadata_xml…or the metadata XML itself sso_url+ca…or the IdP SSO URL plus its signing certificate (PEM) email_attributeOptional: attribute name holding the email, if it is not email/mailsp_certificate+sp_private_keyOptional: key pair for encrypted assertions
Only SP-initiated sign-in (from the Control Center login page) is supported. IdP-initiated tiles in the Okta dashboard are refused.
Connect an OIDC provider
Create a web application in your IdP with the redirect URI
https://<control-center>/api/auth/sso/callback/<connector-id>and set the connector configuration:
| Key | Description |
|---|---|
issuer | Issuer URL, for example https://acme.okta.com or https://accounts.google.com |
client_id | OAuth client ID |
client_secret | OAuth client secret |
scopes | Optional extra scopes (list) |
The Control Center uses the authorization-code flow with PKCE and a nonce, and signs in the user whose email the ID token asserts. An ID token with email_verified: false is refused.
SCIM provisioning
🔒 Business plan and above. SCIM provisioning is part of the Business and Enterprise plans. SAML and OIDC sign-in are not affected. See Control Center features by plan.
SCIM 2.0 (RFC 7643 / 7644) lets your IdP manage Control Center users:
- Create a user when you assign someone to the app. If an account with that email already exists in your workspace, SCIM links it instead.
- Update name and email when they change in the IdP.
- Deactivate (
active = false) or delete a user when you unassign or suspend them. Their access to your workspace is blocked immediately, their sessions in it are ended and issued tokens are revoked. Other workspaces they belong to are not affected. The account row is kept, so tickets and audit history stay intact. - Groups → roles: push groups and map them to Control Center roles.
The workspace owner is never deactivated, deleted or re-roled by SCIM. Such requests fail with a mutability error so you can see them in the IdP’s provisioning log.
Enable SCIM
- Open Settings → SSO and click Enable on the SCIM provisioning card, then name the first token and click Create token. (New token in the SCIM panel creates more.)
- Copy the token. It is shown once. Only a hash is stored. You can create more tokens, rotate a token (the old one stops working at once) or revoke it.
- Note the SCIM base URL:
https://<control-center>/scim/v2.
Okta
- In Okta, open your Quazzar app (or create a SAML app first), then General → App Settings → Edit → Provisioning: SCIM and save.
- On the Provisioning tab → Integration → Edit:
- SCIM connector base URL:
https://<control-center>/scim/v2 - Unique identifier field for users:
userName - Supported provisioning actions: Push New Users, Push Profile Updates, Push Groups
- Authentication Mode: HTTP Header, and paste the SCIM token as the Bearer token
- SCIM connector base URL:
- Click Test Connector Configuration, then Save.
- Provisioning → To App: enable Create Users, Update User Attributes and Deactivate Users.
- Assign people or groups on the Assignments tab. To map roles, push groups on the Push Groups tab.
Azure AD / Microsoft Entra ID
- Enterprise applications → New application → Create your own application (“Integrate any other application you don’t find in the gallery”).
- Provisioning → Get started, and set Provisioning Mode to Automatic.
- Admin credentials:
- Tenant URL:
https://<control-center>/scim/v2 - Secret Token: the SCIM token
- Tenant URL:
- Click Test Connection, then Save.
- Under Mappings, keep Provision Microsoft Entra ID Users and Groups enabled. The default attribute mappings work:
userPrincipalName → userName,mail → emails[type eq "work"].value,Switch([IsSoftDeleted]…) → active. - Assign users and groups under Users and groups, then set Provisioning Status to On.
Azure provisions in cycles of about 40 minutes. Use Provision on demand to test a single user.
Map groups to roles
In Settings → SSO → SCIM provisioning → Group mapping, map IdP group names (case-insensitive) to roles:
| Group (example) | Role |
|---|---|
Quazzar Admins | org_admin |
Quazzar Operators | org_manager |
Teachers | teacher |
A user in several mapped groups gets the highest role. A provisioned user in no mapped group gets the default role (Viewer unless you change it). Changing the mapping re-applies it to all provisioned users. org_owner cannot be mapped. SCIM is the source of truth for provisioned users’ roles: a manual role change in Team is overwritten at the next group change.
On plans below Business
On Community or Pro, the SCIM provisioning card on Settings → SSO carries a Business badge and an Upgrade button, the SCIM panel shows an upgrade card, and the SCIM provisioning switch on Settings → General is locked.
- Your IdP is refused. Every
/scim/v2/*call with a valid token gets403 Forbiddenin the SCIM error format, with adetailthat names the plan (SCIM provisioning is not included in this workspace’s plan (Business or above required)…). We use 403 rather than 402 because RFC 7644 has no 402 and because Okta and Entra ID log a 403 with its detail in the provisioning log instead of treating it as a bad credential, as they do with 401. Entra ID may quarantine the app after repeated failures; restart provisioning after you upgrade. - Tokens stop working but are kept. The token’s last used time and the workspace’s last sync time are not updated. After an upgrade the same tokens work again, so you don’t need to reconfigure the IdP.
- Settings stay readable.
GET /api/sso/scim/status,/configand/tokensanswer with"gated": trueand"required_plan": "business". - Turning SCIM on is blocked. Creating or rotating a token and changing the default role or the group mapping return
402 upgrade_required("feature": "scim"). Turning SCIM off (PUT /api/sso/scim/config {"enabled": false}) and revoking tokens still work on any plan. - Users and groups the IdP already provisioned stay as they are. Manage them by hand under Team until you upgrade.
What the SCIM server supports
| Endpoint | Notes |
|---|---|
GET /scim/v2/ServiceProviderConfig, /ResourceTypes, /Schemas | Discovery |
GET /scim/v2/Users | filter, startIndex, count (max 200), attributes, excludedAttributes |
POST /scim/v2/Users, GET/PUT/PATCH/DELETE /scim/v2/Users/{id} | PATCH with add / replace / remove (Okta and Azure payload styles) |
GET/POST /scim/v2/Groups, GET/PUT/PATCH/DELETE /scim/v2/Groups/{id} | Member add / remove / replace, rename |
Filters support eq ne co sw ew gt ge lt le pr, and, or, not ( … ), grouping, and value paths such as emails[type eq "work"].value eq "[email protected]". Errors use the SCIM error schema, and responses are application/scim+json. Bulk, sort and ETags are not supported. The enterprise extension (department, employeeNumber, …) is stored and returned as sent.
Every SCIM change, including token and mapping changes, is written to the workspace audit log (scim.user.*, scim.group.*, scim.token.*, scim.config.updated).
Operator setup: Google and GitHub for everyone
The operator of a self-hosted Control Center can offer Google and GitHub buttons to all users by setting:
| Variable | Description |
|---|---|
CC_PUBLIC_BASE_URL | Public origin of the Control Center, for example https://cp.example.com (used for redirect and SCIM URLs) |
CC_SSO_GOOGLE_CLIENT_ID / CC_SSO_GOOGLE_CLIENT_SECRET | Google OAuth client. Redirect URI: <base>/api/auth/sso/callback/google |
CC_SSO_GITHUB_CLIENT_ID / CC_SSO_GITHUB_CLIENT_SECRET | GitHub OAuth app. Callback URL: <base>/api/auth/sso/callback/github |
These buttons only sign in existing accounts, matched by verified email.
API
| Method | Path | Auth | Description |
|---|---|---|---|
GET | /api/auth/sso/providers?email=… or ?domain=… | public | {domain, sso, providers: [{id, kind, label, start_url}]} |
GET | /api/auth/sso/start/{provider}?next=/path | public | Redirects to the IdP |
GET | /api/auth/sso/callback/{provider} | public | OAuth / OIDC redirect URI |
POST | /api/auth/sso/saml/{provider}/acs | public | SAML assertion consumer |
GET | /api/auth/sso/saml/{provider}/metadata | public | SAML SP metadata |
POST | /api/auth/sso/exchange | public | {code} → same response as POST /api/auth/login |
GET/POST | /api/sso/login-domains | settings:read / users:manage | List / claim sign-in domains |
POST | /api/sso/login-domains/{domain}/verify | users:manage | Check the DNS TXT record |
DELETE | /api/sso/login-domains/{domain} | users:manage | Remove a domain |
GET | /api/sso/summary | settings:read | [{kind, name, detail, status, users}] for the three cards; the SCIM card adds gated and required_plan below Business |
GET | /api/sso/scim/status | settings:read | {enabled, base_url, token_count, last_sync_at, users_provisioned, groups, gated, required_plan?} |
GET/PUT | /api/sso/scim/config | settings:read / users:manage | {enabled, default_role, group_roles} |
GET/POST | /api/sso/scim/tokens | users:manage | List tokens / create one (plaintext returned once) |
POST | /api/sso/scim/tokens/{id}/rotate | users:manage | Revoke and replace a token |
DELETE | /api/sso/scim/tokens/{id} | users:manage | Revoke a token |
After a successful IdP round trip the browser is redirected to /login/sso#code=…&next=…. The one-time code sits in the URL fragment, so it never reaches server logs. It is valid for 2 minutes and can be used once.