Users endpoint, bearer-token gated, with soft-delete on DELETE.
What’s implemented
The implementation covers user provisioning and theuserName eq "..." filter that identity providers use for lookup. It does not yet cover Groups, Bulk, or the full SCIM filter grammar. It supports the standard Okta and Azure AD provisioning flow for creating and deactivating users.
Note these are mounted at the root path
/scim/v2/..., NOT under /api/auth/. Most IdPs expect SCIM at a top-level path.
Array-filter PATCH paths (emails[primary eq true].value) are explicitly rejected — IdPs that need them should fall back to the equivalent PUT request.
Schema
The user entity needs three SCIM-shaped fields:scimActive=false is the soft-delete signal. Your app code should refuse sign-in or hide the user when this flag is false. Pylon does not auto-revoke sessions on SCIM deactivate. If you want that behavior, watch the User entity for updates and revoke matching sessions in a plugin or scheduled job.
Authentication
Bearer-token gated:PYLON_SCIM_TOKEN set, the env-var pull returns None and check_bearer returns false for every token. Every SCIM request gets 401.
Creating a user
Okta and other IdPs POST a payload conforming to RFC 7643:userName→ no direct mapping; primary email goes toemailemails[primary=true].value→emaildisplayNameorname.givenName + name.familyName→displayNameid→scimId(your IdP’s stable id for the user)active→scimActive
409 with SCIM-shaped error JSON.
Listing users
?filter=userName eq "..." probe is supported (case-insensitive per RFC 7643). Pagination (?startIndex=&count=) is not yet implemented. An unfiltered list returns every row, bounded by your User table size. If you have 50k+ users provisioned via SCIM, plan accordingly.
Soft delete
204 No Content. The row stays in the DB with scimActive: false. Hard delete is your app’s decision, typically a periodic job that hard-deletes rows that have been scimActive: false for N days.
Security guarantees
- Bearer-token gated with constant-time compare against
PYLON_SCIM_TOKEN. - Missing env var = 401 for every request — fail closed, no silent-permissive mode.
- SCIM-shaped error responses (RFC 7644 §3.12) —
schemas: ["urn:ietf:params:scim:api:messages:2.0:Error"],status,detail.
Configuration
https://your-app.com/scim/v2/. The IdP discovers the endpoint by probing. It sends a few test requests, sees SCIM-shaped responses, and confirms.
Where to go next
- SSO — per-org OIDC + SAML, the sign-in side of the same IdP integration
- OIDC Provider — Pylon as IdP for other systems
- API keys — a different “server-to-server” token shape for app integrations