SCIM 2.0 Provisioning¶
ARX implements the SCIM 2.0 protocol (RFC 7644) to enable automated user and group lifecycle management from enterprise identity providers. This guide covers all SCIM endpoints, bearer token setup, user and group operations, group-to-role mapping, and configuration for Okta and Microsoft Entra ID.
Base URL¶
All SCIM endpoints are served under the /scim/v2 path prefix:
https://api.arxsec.io/scim/v2
Authentication¶
SCIM requests are authenticated using a static bearer token, not JWT. Each organization has a dedicated SCIM token hashed with bcrypt and stored in the scim_tokens table.
Include the token in the Authorization header:
Authorization: Bearer <your-scim-token>
The token is validated on every request by comparing the raw token against stored bcrypt hashes. The token is scoped to a single organization -- all user and group operations are restricted to that organization. The last_used_at timestamp is updated on each successful authentication.
Generating a SCIM Token¶
SCIM tokens are provisioned by an administrator. The raw token value should be generated using a cryptographically secure random generator (minimum 32 bytes):
openssl rand -base64 32
Hash the token with bcrypt and store it in the scim_tokens table:
INSERT INTO scim_tokens (org_id, token_hash, description)
VALUES ('<org-id>', '<bcrypt-hash>', 'Okta SCIM provisioning');
Store the raw token securely -- it cannot be recovered from the bcrypt hash.
Endpoints¶
ARX implements 14 SCIM endpoints across discovery, user management, and group management.
Discovery Endpoints¶
| Method | Endpoint | Description |
|---|---|---|
| GET | /scim/v2/ServiceProviderConfig |
Returns service provider configuration and supported features |
| GET | /scim/v2/Schemas |
Returns the User and Group schemas with attribute definitions |
| GET | /scim/v2/ResourceTypes |
Returns supported resource types (User, Group) |
User Endpoints¶
| Method | Endpoint | Description |
|---|---|---|
| GET | /scim/v2/Users |
List users with optional filtering and pagination |
| GET | /scim/v2/Users/{id} |
Retrieve a single user by ID |
| POST | /scim/v2/Users |
Create a new user |
| PUT | /scim/v2/Users/{id} |
Full replacement of a user resource |
| PATCH | /scim/v2/Users/{id} |
Partial update using SCIM PatchOp |
| DELETE | /scim/v2/Users/{id} |
Deactivate a user (soft delete) |
Group Endpoints¶
| Method | Endpoint | Description |
|---|---|---|
| GET | /scim/v2/Groups |
List groups with optional filtering and pagination |
| GET | /scim/v2/Groups/{id} |
Retrieve a single group by ID |
| POST | /scim/v2/Groups |
Create a new group |
| PATCH | /scim/v2/Groups/{id} |
Update group membership and attributes |
| DELETE | /scim/v2/Groups/{id} |
Delete a group and its memberships |
Service Provider Configuration¶
The /scim/v2/ServiceProviderConfig endpoint advertises the following capabilities:
| Feature | Supported |
|---|---|
| Patch | Yes |
| Bulk | No |
| Filter | Yes (max 200 results) |
| Change Password | No |
| Sort | No |
| ETag | No |
| Authentication | OAuth Bearer Token |
User Lifecycle¶
Creating Users¶
When the IdP pushes a new user, ARX creates a record in the users table:
POST /scim/v2/Users
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName": "jane@acme.com",
"name": {
"givenName": "Jane",
"familyName": "Doe"
},
"displayName": "Jane Doe",
"emails": [{"value": "jane@acme.com", "type": "work", "primary": true}],
"active": true,
"externalId": "okta-user-id-12345",
"roles": [{"value": "deployer"}]
}
Key behaviors:
userNameis required and treated as the email address. It must be unique within the organization.- If a user with the same email already exists, a
409 Conflictresponse is returned. externalIdis stored assso_subjectfor identity linking.rolesis optional. If provided, the first role value is applied if it matches a valid ARX role (admin,deployer,auditor,viewer). Otherwise, the default isviewer.displayNameresolution order:displayNamefield, thenname.formatted, thenname.givenName+name.familyName, then the local part of the email.- The
sso_provideris set toscimfor SCIM-provisioned users.
Updating Users¶
Full replacement (PUT) replaces all mutable attributes:
PUT /scim/v2/Users/{id}
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName": "jane@acme.com",
"name": {"givenName": "Jane", "familyName": "Smith"},
"displayName": "Jane Smith",
"active": true,
"externalId": "okta-user-id-12345",
"roles": [{"value": "admin"}]
}
Partial update (PATCH) supports add, replace, and remove operations:
PATCH /scim/v2/Users/{id}
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [
{"op": "replace", "path": "active", "value": false},
{"op": "replace", "path": "displayName", "value": "Jane Smith-Doe"}
]
}
Supported PATCH paths: active, userName, displayName, name.givenName, name.familyName, name.formatted, externalId. Pathless operations with a value object are also supported for bulk attribute replacement, including roles.
Deactivating Users¶
When a user is removed from the IdP application, the IdP sends a DELETE request:
DELETE /scim/v2/Users/{id}
ARX performs a soft delete by setting active=false rather than removing the user record. This preserves audit history and prevents data loss. Returns 204 No Content.
Filtering Users¶
The list endpoint supports SCIM filter syntax for two attributes:
GET /scim/v2/Users?filter=userName eq "jane@acme.com"
GET /scim/v2/Users?filter=externalId eq "okta-user-id-12345"
Pagination¶
Pagination is controlled with startIndex (1-based) and count (default 100, max 200) query parameters.
Group Synchronization¶
Creating Groups¶
When the IdP pushes a group, ARX creates it with an automatic role mapping if the group name contains a recognized role:
POST /scim/v2/Groups
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"],
"displayName": "ARX Deployers",
"externalId": "okta-group-id-67890",
"members": [
{"value": "user-uuid-1"},
{"value": "user-uuid-2"}
]
}
If the group's displayName contains one of the valid role names (admin, deployer, auditor, viewer), ARX automatically sets the role_mapping field. All members of that group will have their ARX role synchronized to match.
Updating Group Membership (PATCH)¶
Adding members:
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [
{"op": "add", "path": "members", "value": [{"value": "user-uuid-3"}]}
]
}
Removing members:
{
"Operations": [
{"op": "remove", "path": "members[value eq \"user-uuid-2\"]"}
]
}
Replacing all members:
{
"Operations": [
{"op": "replace", "path": "members", "value": [{"value": "user-uuid-1"}, {"value": "user-uuid-4"}]}
]
}
Filtering Groups¶
GET /scim/v2/Groups?filter=displayName eq "ARX Deployers"
GET /scim/v2/Groups?filter=externalId eq "okta-group-id-67890"
Deleting Groups¶
DELETE /scim/v2/Groups/{id}
Deleting a group removes all membership records. The users themselves are not affected. Returns 204 No Content.
Group-to-Role Mapping¶
ARX bridges SCIM groups to its RBAC model through the role_mapping field on the groups table.
Automatic mapping: If a group's displayName contains a valid role name (case-insensitive), the mapping is set automatically at creation time.
| Group Display Name | Auto-Mapped Role |
|---|---|
| "ARX Admins" | admin |
| "ARX Deployers" | deployer |
| "Security Auditors" | auditor |
| "ARX Viewers" | viewer |
How role sync works:
- When members are added to a group with a
role_mapping, those users'rolefield in theuserstable is updated to match. - When group membership is replaced or modified via PATCH, all current members have their roles synchronized.
- The
_sync_member_rolesfunction iterates over all group members and updates their roles.
Manual mapping: For groups that do not follow the naming convention, administrators can set the role_mapping field directly in the database.
Configuring Okta SCIM¶
- In the Okta Admin Console, navigate to your ARX SAML/OIDC application.
- Go to the Provisioning tab and click Configure API Integration.
- Check Enable API Integration.
- Set the SCIM connector base URL to
https://api.arxsec.io/scim/v2. - Set Unique identifier field for users to
userName. - Under Authentication Mode, select HTTP Header and paste your SCIM bearer token.
- Click Test API Credentials to verify connectivity.
- Under Provisioning > To App, enable:
- Create Users
- Update User Attributes
- Deactivate Users
- Under Push Groups, add the groups you want to sync (e.g., "ARX Admins", "ARX Deployers").
Configuring Microsoft Entra ID SCIM¶
- In the Azure Portal, navigate to Microsoft Entra ID > Enterprise applications > Your ARX app.
- Go to Provisioning and set Provisioning Mode to Automatic.
- Under Admin Credentials:
- Tenant URL:
https://api.arxsec.io/scim/v2 - Secret Token: Your SCIM bearer token
- Click Test Connection to verify.
- Under Mappings, verify attribute mappings:
userPrincipalName->userNamedisplayName->displayNamegivenName->name.givenNamesurname->name.familyNameSwitch([IsSoftDeleted], , "False", "True", "True", "False")->active- Under Settings, set the scope to Sync only assigned users and groups.
- Set Provisioning Status to On.
Entra ID runs a provisioning cycle approximately every 40 minutes. The initial cycle may take longer depending on the number of users and groups.
Content Type¶
All SCIM responses use the application/scim+json media type as required by RFC 7644.
Error Responses¶
SCIM errors follow the standard format:
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"],
"detail": "User with userName 'jane@acme.com' already exists",
"status": "409",
"scimType": "uniqueness"
}
| Status | Meaning |
|---|---|
| 400 | Invalid request (missing required fields) |
| 401 | Invalid or missing SCIM bearer token |
| 404 | Resource not found |
| 409 | Conflict (duplicate userName or displayName) |
| 500 | Internal server error |
Troubleshooting¶
| Symptom | Cause | Resolution |
|---|---|---|
401 Unauthorized |
Invalid SCIM bearer token | Verify the token matches the bcrypt hash in the scim_tokens table. |
409 Conflict on user creation |
Duplicate userName in the org |
The IdP should use the existing user's ID for subsequent operations. |
| Users created with wrong role | Role mapping not applied | Verify that SCIM groups with role mappings are pushed before or alongside user creation. |
| Entra ID provisioning errors | Attribute mapping mismatch | Check provisioning logs in the Azure Portal and verify mappings. |
| Role not syncing | Group has no role_mapping |
Verify the group displayName contains a valid role name, or set role_mapping manually. |