
SCIM 2.0 Provisioning (RFC 7644) Guide
A technical overview of SCIM 2.0 (RFC 7644) for backend developers and identity engineers, covering user provisioning, deprovisioning, patch operations, and schema management.
SCIM 2.0 Provisioning (RFC 7644)
System for Cross-domain Identity Management (SCIM 2.0), defined in RFC 7644, is often misunderstood as a general-purpose user database API. It is not. SCIM is a synchronization protocol designed to automate the exchange of user identity information between identity providers (IdPs) like Okta or Azure AD and service providers (SPs) like Salesforce or Slack. For backend engineers, the critical distinction is that SCIM defines a transport mechanism for identity state, not the identity source itself. Implementations must treat SCIM endpoints as immutable mirrors of the authoritative source of truth, ensuring that every write operation reflects a deliberate state change from the IdP.
The Core Mechanism: Resource Identification
SCIM operates on a strict resource model where every entity (User, Group, Role) is identified by two distinct keys. Confusing these leads to race conditions and duplicate records.
id: A server-generated, immutable unique identifier for the resource within the SCIM provider. This is analogous to a database primary key.userName: A unique identifier for the user as defined by the provisioning client (the IdP). This is analogous to a natural key or external reference.
When an IdP provisions a user, it sends a POST request to the /Users endpoint. The server validates the payload against the User schema (RFC 7643), assigns a new id, and returns the created resource. Crucially, subsequent updates from the IdP use the userName to locate the resource, not the id. This decoupling allows the SCIM provider to change its internal storage mechanisms without breaking the sync channel.
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName": "bjensen@example.com",
"name": {
"formatted": "Ms. Barbara J Jensen, III",
"familyName": "Jensen",
"givenName": "Barbara"
},
"emails": [
{
"value": "bjensen@example.com",
"type": "work",
"primary": true
}
],
"id": "2819c223-7f76-453a-919d-413861904646",
"meta": {
"resourceType": "User",
"created": "2019-03-10T05:22:49Z",
"lastModified": "2019-03-10T05:22:49Z",
"location": "https://example.com/v2/Users/2819c223-7f76-453a-919d-413861904646"
}
}Provisioning Workflow: Lifecycle Management
The SCIM protocol maps standard HTTP verbs to specific provisioning actions. Understanding the atomicity of these operations is essential for data consistency.
Create (POST): Creates a new resource. The server must validate all required attributes (e.g., userName) before persisting. If the userName already exists, the server must return 409 Conflict.
Read (GET): Retrieves a resource by id or filters by userName. The response includes the full resource representation unless filtered.
Update (PUT): Replaces the entire resource state. This is a dangerous operation if misused. If a client sends a PUT request with only a subset of attributes, the server must delete all other attributes that were previously present. For example, if a User has multiple email addresses and the client sends a PUT with only one email, the others are lost. Use PUT only when the client possesses the complete, current state of the resource.
Delete (DELETE): Removes the resource. The server should return 204 No Content on success. In identity management, this often triggers a hard delete in the target system, which may have compliance implications. Many systems implement a soft-delete mechanism where the active attribute is set to false instead of physical deletion.
The Patch Operation: Atomic Multi-Attribute Updates
The most common source of bugs in SCIM implementations is the handling of PATCH requests. Unlike PUT, PATCH is designed for partial updates. However, SCIM does not support arbitrary field updates; it requires explicit instructions on how to modify the resource.
A PATCH request contains a Operations array. Each operation specifies an op (add, remove, replace) and a path (the attribute to modify). The server must process these operations in the order they appear in the array.
Consider a scenario where we need to change a user's manager and add a new phone number. The request body looks like this:
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [
{
"op": "replace",
"path": "title",
"value": "Senior Engineer"
},
{
"op": "add",
"path": "phoneNumbers[type eq 'mobile']",
"value": {
"value": "555-1234",
"type": "mobile"
}
},
{
"op": "remove",
"path": "emails[type eq 'home']"
}
]
}The path attribute supports complex filtering using the SCIM Filter Language (RFC 7644). In the example above, phoneNumbers[type eq 'mobile'] targets the specific multi-valued attribute where the type is mobile. If no match is found, the add operation fails with a 400 Bad Request unless the path is omitted (in which case it appends to the end of the list).
Mechanism Warning: The server must validate the entire Operations array before applying any changes. If one operation fails (e.g., invalid value for title), the entire request must be rolled back. This ensures atomicity: either all changes are applied, or none are.
Schema Management: Extensibility
SCIM defines core schemas for User, Group, and Role, but real-world systems require custom attributes. SCIM 2.0 handles this through schema extension, defined in RFC 7643.
Each resource includes a schemas array listing the URNs of all schemas it adheres to. The core User schema is urn:ietf:params:scim:schemas:core:2.0:User. To add custom fields, a vendor defines a custom schema (e.g., urn:ietf:params:scim:schemas:extension:enterprise:2.0:User) and includes it in the schemas array.
{
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:User",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"
],
"userName": "bjensen@example.com",
"employeeNumber": "12345",
"department": "Engineering",
"id": "2819c223-7f76-453a-919d-413861904646"
}The schemas array included in the resource indicates which schemas are supported by the server. Clients can query the /Schemas endpoint to discover available extensions. This mechanism allows SCIM to remain flexible without sacrificing interoperability.
Conclusion
Implementing SCIM 2.0 requires strict adherence to its resource model and operation semantics. The key takeaways for backend engineers are:
- Separate
idanduserName: UseuserNamefor lookup andidfor internal tracking. - Respect
PUTvsPATCH: UsePUTfor full replacement,PATCHfor atomic partial updates. - Validate Entirely: Ensure
PATCHoperations are atomic; roll back on any failure. - Use Schemas: Leverage extension schemas for custom attributes to maintain compatibility.
By treating SCIM as a synchronization protocol rather than a database API, engineers can build reliable identity management systems that scale with organizational complexity.
Related posts
The Seven Registered Claims: iss, sub, aud, exp, nbf, iat, jti
A technical breakdown of the seven standard JWT claims: iss, sub, aud, exp, nbf, iat, and jti, explaining their roles in authentication and authorization.
SSO With Cookies Across Multiple Domains
Explore how to implement Single Sign-On using cookies across multiple domains, addressing cross-domain challenges and third-party cookie restrictions.
Spring Security ABAC: Implementing AuthorizationManager
A technical guide on implementing Attribute-Based Access Control in Spring Security using AuthorizationManager, MethodSecurity, and external PDPs.