SCIM
SCIM 2.0 provisioning — Users + Groups REST endpoints so an enterprise IdP (Okta, Entra, OneLogin) can create / update / deactivate users in your app.
@voltro/plugin-scim exposes the SCIM 2.0 endpoints enterprise IdPs use to provision users into your app. When an admin adds someone to your app in Okta/Entra, the IdP POSTs to your /scim/v2/Users endpoint; when they remove them, it PATCHes active: false. This is a hard requirement in most enterprise procurement.
Wiring
// app.config.ts
import { scimPlugin } from '@voltro/plugin-scim'
export default {
type: 'api' as const, name: 'api',
plugins: [scimPlugin({ token: process.env.SCIM_TOKEN! })], // basePath defaults to /scim/v2
}Mounts /scim/v2/Users + /scim/v2/Groups, bearer-gated by token (paste it into the IdP's SCIM config). Contributes the scim_users + scim_groups tables.
Endpoints
| Method + path | SCIM operation |
|---|---|
GET /scim/v2/Users?filter=userName eq "x" |
list / find a user (userName / externalId eq filters, startIndex/count pagination) |
GET /scim/v2/Users/<id> |
read a user |
POST /scim/v2/Users |
create (provision) — duplicate userName answers 409 uniqueness (DB-unique, IdP-retry-safe) |
PUT /scim/v2/Users/<id> |
replace |
PATCH /scim/v2/Users/<id> |
partial update — incl. the active:false deactivation IdPs send |
DELETE /scim/v2/Users/<id> |
delete |
GET/POST/DELETE /scim/v2/Groups |
list (displayName / externalId eq filters, paginated) / create / delete groups |
PUT/PATCH /scim/v2/Groups/<id> |
replace / membership add-remove-replace (how Okta/Entra push membership changes) |
GET /scim/v2/ServiceProviderConfig · /Schemas · /ResourceTypes |
the RFC 7644 §4 discovery trio IdPs probe at connector setup |
Requests + responses use the SCIM JSON envelope (schemas, id, meta, ListResponse); an unsupported filter is answered 400 invalidFilter (never silently unfiltered). The pure mapping (toScimUser, fromScimUser, applyScimPatch, applyScimGroupPatch, scimList, parseScimFilter) is exported + unit-tested.
Notes
- The bearer token is the trust boundary — keep it secret + rotate it via your IdP.
scim_usersis the SCIM-side mirror; map it to your realuserstable inonCreate/sync logic if they differ.
Permissions
store:write (the SCIM tables).