Security

SCIM provisioning

Vigilo implements SCIM 2.0 (WC.2) for automated user and group lifecycle, including create / read / update / delete on Users and Groups, ETag-based…

Last updated

Overview

Vigilo implements SCIM 2.0 (WC.2) for automated user and group lifecycle, including create / read / update / delete on Users and Groups, ETag-based optimistic concurrency via If-Match, startIndex-based pagination, and meta.lastModified timestamps. Combined with SAML or OIDC (covered separately), SCIM lets an IdP like Okta or Azure AD own the entire identity surface — Vigilo never holds an authoritative list of who should be a member, only who currently is.

The SCIM endpoints live under /scim/v2/ and authenticate via a ScimToken, a long-lived bearer token issued from Settings → SCIM → New token. Each workspace runs its own SCIM endpoint with its own token, so an IdP can provision multiple Vigilo workspaces independently.

Why it exists

Manual member management at scale is impossible. A 500-person engineering org might have:

  • A new joiner every week who needs the right workspaces with the right roles
  • A leaver every two weeks who needs every workspace revoked atomically (failing to do so on a deprovision is a SOX-level audit finding)
  • Frequent team moves where roles in some workspaces change
  • Custom apps where membership is determined by an IdP group like eng-prod or cert-operators

SCIM puts the IdP in charge of all of this. The Vigilo admin focuses on policy ("members of the eng-prod group get the approver role in workspace prod") rather than data entry.

Endpoints (SCIM 2.0)

All endpoints accept and return application/scim+json. Standard 2.0 envelopes (schemas, id, meta) apply.

Users

  • POST /scim/v2/Users/ — create a user. Body: SCIM User schema. Returns 201 with the created user plus an ETag header. If email matches an existing user, returns 409 conflict per spec.
  • GET /scim/v2/Users/?startIndex=1&count=100&filter=userName eq "alice@acme.com" — list with pagination and filter. startIndex is 1-based per SCIM. Filter supports eq, ne, co, sw, pr on userName, emails.value, active.
  • GET /scim/v2/Users/{id} — read single. Returns ETag header.
  • PUT /scim/v2/Users/{id} — full replace. Requires If-Match: <etag>; mismatch returns 412 Precondition Failed.
  • PATCH /scim/v2/Users/{id} — partial update via Operation list. Common ops: set active: false to soft-deprovision.
  • DELETE /scim/v2/Users/{id} — hard delete. Most IdPs prefer to set active: false instead.

Groups

  • POST /scim/v2/Groups/ — create a group.
  • GET /scim/v2/Groups/ — list.
  • GET /scim/v2/Groups/{id} — read.
  • PUT /scim/v2/Groups/{id} — full replace including members list.
  • PATCH /scim/v2/Groups/{id} — partial update. Most IdPs PATCH group membership as the user joins/leaves.
  • DELETE /scim/v2/Groups/{id} — hard delete.

ServiceProviderConfig

  • GET /scim/v2/ServiceProviderConfig — capability advertisement. Vigilo advertises: PATCH supported, ETag supported, filter supported, pagination supported (50 default, 1000 max), bulk NOT supported.

ScimToken auth

ScimToken is a row in the database with workspace, token_hash, created_by, created_at, last_used_at. The plaintext token is shown once at creation (prefixed scim_) and is hashed at rest. Every SCIM request must include Authorization: Bearer scim_...; mismatch returns 401.

Tokens never expire automatically (IdPs are typically long-lived integrations). Rotate manually from Settings → SCIM; deleting the old token after the IdP picks up the new one.

JIT provisioning on group membership (WB.12)

When an IdP adds a user to a SCIM group, Vigilo can automatically create the corresponding WorkspaceMembership based on OIDCGroupMapping rows. This is just-in-time (JIT) provisioning: the user never has to manually accept a workspace invite, and the workspace never has stale members an admin forgot to remove.

The flow:

  1. IdP PATCHes /scim/v2/Groups/{id} to add user X to group eng-prod.
  2. SCIM endpoint creates the Group membership record.
  3. Vigilo looks up OIDCGroupMapping rows where group_name='eng-prod'. For each match (which has a target workspace and role), Vigilo upserts a WorkspaceMembership for user X with that role.
  4. If the user already has a membership with a different role, the JIT update replaces it. The audit log records both the old and new role.

The reverse is symmetric: removing a user from the SCIM group removes the corresponding WorkspaceMembership (unless that membership was created manually outside JIT, which is preserved).

OIDCGroupMapping (WB.13)

OIDCGroupMapping is the rule table that connects IdP groups to workspace memberships. Fields:

  • group_name — IdP-side name (matched against SCIM Group displayName or OIDC token groups claim)
  • workspace — target workspace
  • role — target built-in role
  • custom_role — optional FK if you want to grant a custom role instead

Mappings are workspace-scoped, but a single group_name can have mappings in many workspaces — useful when an IdP group covers multiple Vigilo workspaces (e.g. eng-leadership should be admin in engineering-tools AND viewer in marketing-tools).

When a SCIM group changes, all matching mappings are evaluated, so one IdP group update can sync membership across many workspaces.

Common workflows

1. Configure Okta to provision Vigilo

  1. In Vigilo: Settings → SCIM → New token. Copy the scim_... token.
  2. In Okta: Applications → Add SCIM 2.0 app. Base URL: https://{vigilo-host}/scim/v2/. Auth: Bearer, paste the token.
  3. In Okta: enable Create, Update, Deactivate user push. Enable Push Groups.
  4. In Vigilo: Settings → OIDC group mappings → New mapping. Map IdP group eng-prod to workspace prod with role engineer.
  5. Assign Okta groups to the SCIM app. Within a few minutes, members appear in the right Vigilo workspaces.

2. Configure Azure AD

Same shape as Okta. Azure AD calls SCIM "Provisioning" inside the Enterprise Application; otherwise identical.

3. Audit a deprovisioning

  1. Open the audit log filtered by target_type=WorkspaceMembership and action=delete.
  2. Each row shows the SCIM token name that initiated the delete and the underlying group change that triggered it (via the metadata JSON field).

4. Rotate the SCIM token

  1. Settings → SCIM → New token. Update the IdP to use the new token.
  2. Watch Settings → SCIM → Tokens for last_used_at to confirm the IdP has switched.
  3. Delete the old token.

Permissions

Action Role
Create / delete SCIM tokens Owner
Manage OIDCGroupMapping rows Admin / Owner
SCIM API calls themselves The token's workspace scope only

A SCIM token is workspace-scoped — it can only provision members into the workspace that created it. There is no installation-wide SCIM token.

Troubleshooting

Okta says "Provisioning failed: 401". Token wrong or workspace mismatch. Confirm the token in Settings → SCIM matches what Okta has.

Members appear but with the wrong role. Check OIDCGroupMapping. Missing mapping → default to viewer. Multiple mappings for the same group → the most recently created one wins.

Deprovisioning didn't remove the user from one workspace. That membership was created manually, not via JIT. Manual memberships are preserved on group removal by design — an admin must delete them explicitly. Look for membership rows where metadata.created_by_jit=False.

Group PATCH returns 412 Precondition Failed. Stale If-Match. IdP re-reads the group, retries with the new ETag — most IdPs handle this automatically.

Filter expression returns 400. Vigilo supports eq, ne, co, sw, pr and AND/OR. More exotic filters (e.g. complex grouping) are not supported; the IdP may be using an extended syntax. Configure the IdP to use only the basic operators.

Related articles