Skip to content

Enterprise · Single sign-on and SCIM

SCIM provisioning

With SCIM, the identity provider owns every account, a deactivation at the IdP ends FluxGate access at once, and pushed groups can drive roles before anyone signs in.

Video coming soon

SCIM provisioning

Before you start

  • Your license includes Single sign-on and SCIM provisioning (Enterprise, or Business with both add-ons). Check it under Settings, License.
  • An SSO provider exists for the IdP: OpenID Connect or SAML 2.0. It can stay disabled while you set up SCIM.
  • public_base_url is set in config.toml, and the IdP can reach that address.
  • At least one enabled break-glass admin with a password exists. FluxGate refuses to create a SCIM token without one.
  • Sign in as admin System admin.

For a complete, tested walk-through with Okta (sign-in app, provisioning app, groups and the full lifecycle), see FluxGate with Okta.

How SCIM works in FluxGate

FluxGate is a SCIM 2.0 service provider (RFC 7643 and RFC 7644). The IdP calls FluxGate whenever a person or a group changes. Nobody touches FluxGate to add, change or remove an account.

  • One SCIM service per SSO provider. Each SCIM token belongs to one provider. The token decides which provider a request is for. A token reaches the SCIM endpoints and nothing else.
  • SCIM-managed users. A user that the IdP created through SCIM is SCIM-managed. A token sees and changes only the SCIM-managed users of its own provider. Local users, break-glass admins, system clients and users of other providers do not exist for it.
  • No passwords. SCIM users sign in through SSO only. They have no FluxGate password.
  • The SSO switch does not stop SCIM. Enabled on the provider controls sign-in only. You can provision people before you turn sign-in on, and deactivations keep arriving while sign-in is off. To stop SCIM, revoke the provider's tokens.

The service covers Users and Groups. Bulk operations, /Me, sorting, ETags and password sync are not supported, and the discovery endpoint ServiceProviderConfig says so.

The SCIM base URL

SCIM base URL
<public_base_url>/api/v1/scim/v2

For Juniper Market that is https://fluxgate.example.com/api/v1/scim/v2. The IdP calls these endpoints under it:

EndpointPurpose
GET /ServiceProviderConfig, GET /ResourceTypes, GET /SchemasDiscovery
/Users, /Users/<id>Create, read, replace, update and delete users
/Groups, /Groups/<id>Create, read, replace, update and delete groups

Requests carry the token as a bearer token. Bodies may be application/scim+json or application/json, up to 1 MiB. Without public_base_url, the Provisioning dialog shows "Set public_base_url in the backend config to show the SCIM base URL." and Location headers use the Host header of the request.

  1. Open Settings, Single Sign-On. On the row okta, click the Provisioning icon. The dialog SCIM provisioning for Okta opens.
  2. Next to SCIM base URL, click Copy base URL. Paste it into the IdP's provisioning settings.
  3. Under Tokens, click Create token. Type a Token name, for example okta. Set Expires (optional) if your policy needs an expiry. Click Create.
  4. Token created shows the token once, with the hint "Copy the token now and store it in your identity provider." Click Copy token, paste it into the IdP, and click Done.
  5. Under Link first sign-in to a provisioned user, type the Login claim and choose the SCIM attribute. For Okta: sub and externalId. See the next section.
  6. Click Save link. The toast "Link settings saved" appears.
  7. In the IdP, run its connection test. Back in FluxGate, reopen the dialog: Last request shows the test, Last error shows None, and the token shows when it was last used.

Tokens

  • A token starts with fgscim_. FluxGate shows it once and stores only a hash. The token list keeps the name, the prefix, when it was created, when it was last used, and its expiry.
  • A provider has at most two active tokens (409 scim_token_limit). Two tokens let you rotate without a gap.
  • FluxGate accepts the token with or without the Bearer scheme. Never type Bearer into an IdP field that adds it itself.
  • A revoked or expired token gets 401 at once. Revoking works in every license state, also in ReadOnly.
  • The token list warns about a token that expires within 30 days ("Expires in 12 days."), an expired token ("Expired. The IdP gets 401 with this token."), and a token created more than a year ago ("Created more than a year ago. Rotate this token.").

To rotate a token:

  1. Create a second token and paste it into the IdP.
  2. Check that Last used of the new token moves when the IdP sends a request.
  3. Click Revoke on the old token and confirm with Revoke token. The toast "Token revoked" appears. The old token cannot be restored.

A person that SCIM creates has no SSO identity until their first sign-in. At that sign-in, FluxGate looks for their SCIM user with a pair you choose per provider:

  • Login claim: a claim of the sign-in. For OIDC, FluxGate reads it from the ID token, then from userinfo. Default email.
  • SCIM attribute: email, externalId or userName. Default email.
Identity providerLogin claimSCIM attributeWhy
Okta (OIDC)subexternalIdOkta sends its user ID (00u…) as externalId, and the same ID is the sub of every sign-in. It survives email changes. The default pair also works, because Okta sends email_verified.
Microsoft Entra ID (OIDC)oidexternalIdEntra sends no email_verified, so the default pair never links. Map externalId from objectId in Entra. This is also the safe choice for a multi-tenant issuer.
SAML providersAn attribute name, or name_idexternalIdThe login claims are the assertion attributes by their exact names, plus email (the resolved email) and name_id. There is no sub. To link on a stable ID, have the IdP send it as an attribute.

Rules:

  • The match needs a live SCIM user of this provider. A match on email or userName ignores case. A match on externalId is exact.
  • A match links without Link to existing accounts by email, because the SCIM record came from the same IdP. The audit entry sso_identity_linked carries "via": "scim".
  • When the login claim is email, the sign-in needs a verified email, whatever SCIM attribute you pair it with. For SAML, Assume IdP emails are verified decides. Allowed email domains still runs first.
  • If no SCIM user matches, the sign-in continues as before: linking by email, then account creation.

Users

FluxGate stores these SCIM attributes:

SCIM attributeIn FluxGate
idThe FluxGate user ID. Read-only.
userNameRequired, unique per provider, ignoring case. A change does not rename the FluxGate username.
externalIdOptional, unique per provider.
name.givenName, name.familyNameFirst and last name. They fall back to displayName, then the part of the email before the @.
displayNameStored and returned.
emailsThe primary email, else the first. Required and unique across all users. Its type must be work or missing.
activeThe IdP's active flag. Missing on create means true.
groupsThe groups of the user. Read-only: membership changes go through /Groups.

FluxGate ignores other attributes, such as title, phoneNumbers and the enterprise extension (department, manager). A change to password fails with 400 invalidPath. Filters support eq on userName, externalId, id, emails.value and active, joined with and. Paging uses startIndex and count (default 100, at most 200).

In Settings, Users, people created by SCIM carry a SCIM badge next to the SSO badge.

Users list where leo.garcia, maya.chen, nina.patel and omar.haddad at junipermarket.example carry SSO and SCIM badges, while the local users admin, priya and sam have none (opens full size in a new tab)
People that Okta provisioned carry SSO and SCIM badges. The local users admin, priya and sam sign in with a password.

The IdP owns the profile

An admin cannot change the first name, last name or email of a SCIM-managed user: the change fails with 409 scim_managed, and the user page shows the chip "Managed by SCIM provisioning". Roles and teams follow the rules for SSO-managed users. An admin cannot set or reset a password for a SCIM user.

The user lifecycle

Juniper Market hires maya.chen@junipermarket.example, moves her between groups, and later offboards her. Each step happens in the IdP. FluxGate follows.

In the IdPFluxGate receivesResult in FluxGateAudit entry
Joiner: assign Maya to the app, or to an assigned groupA lookup by userName, then POST /UsersAccount created, enabled, without a password and not an admin. With role sync SCIM, she gets the roles and teams of her groups before she signs in.scim_user_created
Profile change: change her last name or emailPUT or PATCH /Users/<id>Name or email updated.scim_user_updated
Mover: move her from FluxGate-Requesters to FluxGate-ApproversPATCH /Groups/<id> on both groupsWith role sync SCIM, Requester is removed and Approver added, from her next request.scim_group_updated, sso_role_sync
Leaver: deactivate Mayaactive set to falseAccount disabled. Every access and refresh token revoked in one transaction.scim_user_deactivated
Rehire: activate Maya againactive set to trueThe same account enabled again, with her roles. No second account.scim_user_reactivated
Delete (Entra ID, after a soft delete)DELETE /Users/<id>Account disabled, SCIM management ends, the user row stays.scim_user_deleted

What a deactivation does

  • FluxGate disables the user and revokes all access and refresh tokens in one transaction. The person's next request fails with 401, and a token refresh fails.
  • Open live streams of that person, such as the live feeds in the UI, close at once.
  • The account, its linked identities and its history stay. Every change the person made stays in the audit log.
  • A deactivation always applies. If the same request also carries profile values that FluxGate refuses (for example an email that belongs to another user), FluxGate applies the deactivation alone and answers 200. The audit entry notes "profile_change_skipped": "invalid", and Last error reads profile change skipped on deactivation.
  • After a DELETE, a later create with the same externalId or userName links the same user again.

Admin disable and enable

An admin can still disable a SCIM-managed user under Settings, Users. This is a kill switch that sticks: an IdP push with active true never lifts it.

  • Enable lifts only the admin's disable. If the IdP's last state is inactive, the user stays disabled until the IdP reactivates them.
  • A user that the IdP deactivated cannot be enabled by an admin (409 scim_managed). Reactivate the person in the IdP.
  • The user page shows who disabled the user: an admin, the identity provider, or both.

Existing accounts

When a create carries the email of an existing user, FluxGate adopts that user only if it already has an identity at the same provider, is not an admin, and is not a system user (scim_user_adopted). Any other owner of the email fails with 409 uniqueness. Local users are never adopted.

The last enabled admin

SCIM never leaves FluxGate without an enabled admin. If a deactivation or delete would disable the last enabled admin, FluxGate changes nothing, answers 409, writes scim_deactivation_blocked, and shows system admins a banner. Make another admin, then let the IdP retry.

Groups and the SCIM role sync mode

The IdP can push groups to /Groups: a displayName, an optional externalId, and the members. Members must be SCIM users of the same provider, so push the users first. FluxGate stores every pushed group. Whether a group changes access depends on the role sync mode of the provider:

Role sync modeAt SSO sign-inWhen the IdP pushes a group change
Authoritative or AdditiveSync from the groups in the token or assertionStored only. The Provisioning dialog warns "Pushed groups are not used. Set role sync to SCIM to apply them."
OffNothingStored only, with the same warning
SCIMNothing: sign-in sync is skippedAuthoritative sync for every affected user

With SCIM, the groups of a user are the displayName and the externalId of every group the user is in. A mapping's group value matches either one, exactly and case-sensitive. Okta mappings use group names, such as FluxGate-Checkout. Entra ID mappings use group object IDs, which Entra sends as externalId. The same rules apply as for sign-in sync: manual assignments win, sync revokes only an admin flag it granted, and it never removes the last enabled admin.

A resync happens when:

  • a member is added or removed: that user;
  • a group is renamed or its externalId changes: every member;
  • a group is deleted: its former members;
  • a SCIM user is deleted: that user;
  • the provider switches to SCIM, or its group mappings change while in SCIM: every SCIM user of the provider.

A change applies from the person's next request. They do not sign in again.

Switch to the SCIM role sync mode

  1. Create a SCIM token and let the IdP push users and the groups your mappings use.
  2. Open Group mappings and check that every group value equals a pushed group name (or, for Entra ID, a group object ID).
  3. Open Edit provider, set Role sync mode to SCIM (groups pushed by the IdP) and click Save changes. The row shows SCIM under Role sync.
  • The switch needs the SCIM entitlement and an active token. Without a token, the form shows "Create a SCIM token first." and saving fails with 409 scim_token_required. You cannot create a provider directly in the mode SCIM.
  • The switch resyncs every SCIM user at once, from the groups already pushed. Roles and teams that sign-in sync gave them and that no pushed group maps to are removed. Push the groups before you switch, so nobody loses access in between.
  • SSO users of the provider that SCIM does not manage are not resynced. They keep their sign-in roles until an admin removes them.
  • Switching away from SCIM needs no entitlement. Assignments that SCIM made stay, and sign-in sync takes over from the next sign-in.
  • FluxGate does not create roles or teams from pushed groups. Groups map to existing roles and teams through the mappings.

Licensing and ReadOnly

Offboarding keeps working when the license lapses. Without the SCIM entitlement, and in ReadOnly:

Still worksRefused with 403 and license_required: scim
Every readCreating users and groups
Deactivating and deleting usersReactivating users
Removing group members and deleting groupsProfile changes, group renames, adding members
Revoking SCIM tokensCreating SCIM tokens

A request that deactivates and changes the profile applies only the deactivation, and the audit entry notes "profile_change_skipped": "license". The IdP sends the profile again once the license is back. Without the entitlement, a resync only removes access; the next licensed resync adds what is missing.

Audit entries

Every SCIM change is in the audit log with the actor scim:<provider slug>, for example scim:okta:

EntryWhen
scim_user_created, scim_user_updated, scim_user_adoptedA user is created, changed or adopted.
scim_user_deactivated, scim_user_reactivated, scim_user_deletedA user is deactivated, activated again or deleted.
scim_group_created, scim_group_updated, scim_group_deletedA group changes.
sso_role_sync with "source": "scim"A group change changed roles, teams or admin.
scim_deactivation_blockedA deactivation would have removed the last enabled admin.
scim_license_refusedA change was refused for the license.
scim_token_created, scim_token_revokedAn admin created or revoked a token. The actor is the admin.

Entries list the names of changed attributes, never their values, and never a token. See Audit log.

Limits

  • Rate limits per token: 600 requests a minute with a burst of 300 by default. Over the limit, FluxGate answers 429 with Retry-After. Okta and Entra ID wait and retry.
  • Wrong or missing token: 30 requests a minute per client address by default, so one client with a bad token does not lock out another.
  • First sync: about two requests per user plus one or more per group. At the default limit, 10,000 users take about 35 minutes. Raise the limits for a faster first sync:
config.toml
[scim]
requests_per_minute = 600
burst = 300
unknown_token_per_minute = 30
  • Group members: at most 1000 member values in one request (400 tooMany).
  • Large groups: a rename resyncs every member in one transaction, so it takes longer for a group with thousands of members.

Identity provider notes

Okta

Okta needs two classic app integrations assigned to the same groups: an OIDC app for sign-in and a SWA app that carries SCIM. The Okta Integration Wizard that combines both does not show the OIDC client secret. Leave Sync password off. Okta never deletes users; it deactivates them. The full setup is in Set up provisioning and Groups, roles and teams.

Microsoft Entra ID

This setup follows the vendor documentation and was not run against a live tenant.

  1. In Enterprise applications, open your application, then Provisioning. Set the mode to Automatic.
  2. Set Tenant URL to the SCIM base URL and Secret token to the fgscim_ token. Click Test Connection.
  3. Under Settings, set Scope to Sync only assigned users and groups, then assign users and groups.
  4. Under Mappings, keep Provision Microsoft Entra ID Users. Keep Provision Microsoft Entra ID Groups only if pushed groups should drive roles, with group object IDs as mapping values.
  5. In the user mapping, keep the matching attribute userName. Map externalId from objectId and emails[type eq "work"].value from mail.
  6. In FluxGate, set the link pair to oid and externalId.
  7. Use Provision on demand for one user, then start provisioning. Entra runs a cycle about every 40 minutes. The first cycle takes longer.

Entra deactivates first and keeps the user readable. It sends a delete later, which ends SCIM management.

Troubleshooting

The Provisioning dialog shows the Last request and the Last error of the provider: status, SCIM error type and detail, never the request body.

401 on every call

The token is wrong, revoked or expired, or it is not an fgscim_ token. Okta's catalog app SCIM 2.0 Test App (Header Auth) sends Bearerfgscim_… if you type Bearer in front. Create a new token and paste only the token.

409 uniqueness on create

The userName or externalId is in use, or the email belongs to a user that SCIM does not manage. Remove the duplicate, or let the person keep the local account.

409 "last enabled admin"

The user is the last enabled admin. Make another admin, then let the IdP retry.

409 scim_requires_local_admin when you create a token

No enabled break-glass admin with a password exists. Create one first.

400 invalidValue "the email must have type work or no type"

The IdP sends the email with another type. Map it to emails[type eq "work"].value.

400 invalidPath on password

Password sync is on in the IdP. Turn it off: SCIM users sign in through SSO.

The link pair does not match, or the login claim is email and the IdP sends no verified email. Use sub and externalId for Okta, oid and externalId for Entra ID.

Pushed groups change no roles

The role sync mode is not SCIM, or no group value equals the group's displayName or externalId. For Entra ID, use the object ID.

A user stays disabled after the IdP reactivated them

An admin disabled the user. Click Enable on the user in FluxGate.

Location headers show the wrong host

public_base_url is not set. Set it and restart the backend.

Next

See the complete Okta setup, from sign-in to leaver, in FluxGate with Okta.