[Openid-specs-fastfed] FastFed SCIM Interop requirements
McAdams, Darin
darinm at amazon.com
Wed Feb 12 02:18:44 UTC 2020
At the prior WG meeting, we discussed SCIM interoperability questions. I had an action to come back with a strawman proposal.
This proposal is pasted below and also uploaded into our Google Drive:
https://drive.google.com/file/d/1vlozxI74WYxUK69IlvhaXaiclVJYHLJH/view?usp=sharing
If possible, let’s use tomorrow’s WG meeting to review together. For folks who can’t attend the meeting, take a look and drop comments into the Google doc.
-Darin
SCIM Interop Requirements
The FastFed working group deliberated the topic of prescriptiveness for SCIM interoperability.
Based on collective experience, we have observed that multiple ways exist to perform the same actions in SCIM. For example, there are at least 3 different mechanisms to update a User object. Identity Providers don’t share the same approaches, thereby putting burden on Application Providers to deeply study each IdP and (in the experience of AWS) apply 1 year of effort to universally support all of them.
When Applications cannot make this level of investment, end-users find interop barriers when integrating between Providers.
To fulfill the FastFed UX promise of “click a button and it just works”, we believe it is necessary to offer more prescriptive guidance to FastFed Compliant SCIM providers, akin to the FastFed guidance for other protocols such as SAML. While this discussion may be more at home in a SCIM working group, the FastFed timelines encourage a more rapid response.
Due to the variability across Identity Providers, there is near certainty that any proposed interop profile will not completely match how IdPs behave today; necessitating work to comply.
What we don’t know is whether the effort to comply is minimal, or launch-blocking. To help inform our decision making, this document presents a strawman proposal for SCIM interop. The intent is for working group members to evaluate (a) whether they agree with the approach, and (b) the effort for compliance.
This information will facilitate future working group discussion.
For ease of consumption, this document forgoes the vocabulary of normative specifications and simply enumerates various CRUD operations and recommended implementation patterns.
User Operations
________________________________
Create User
SCIM Operation: POST /Users
Example:
POST /Users HTTP/1.1
Host: example.com
Accept: application/scim+json
Content-Type: application/scim+json
Authorization: Bearer h480djs93hd8
Content-Length: ...
{
"schemas":[
"urn:ietf:params:scim:schemas:core:2.0:User",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"
],
"externalId": "98d78581-dd0d-4361-ab61-9511c6e5f035",
"userName":"bjensen",
"externalId":"bjensen",
"name":{
"formatted":"Ms. Barbara J Jensen III",
"familyName":"Jensen",
"givenName":"Barbara"
}
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {
"costCenter":"12345",
"manager":{"value": "0cca76a8-090a-4944-8e61-e7791e619d48"}
}
}
Notes:
* The “groups” attribute on the User object must NOT be included
* Multi-value elements, such as Emails, must have at least 1 member with “primary=true”
* References to other resources, such as the “manager” attribute in the EnterpriseUser object, must contain the “id” of the referenced object in the SCIM server. The referenced object must be created in advance. (Technical note: this precludes circular references. The SCIM bulk operation is intended to help with this, but we’re calling it out of scope. Does anyone need circular references?)
* If a value for “password” is sent, and the Application doesn’t need a password (because it relies on authentication via the IdP), it should ignore the password. (For example, Okta generates an app-specific password for every user and sends it in the SCIM object, even if it’s unneeded.)
Update User
To illustrate the complexity of updates, there are (at least) 9 distinct use cases:
* Replace the object completely
* Add/Update/Remove a simple attribute
* Add/Replace/Remove the full contents of a complex attribute
* Add/Remove members of a multi-value attribute
As a reminder, SCIM provides several “op” methods to handle this variety:
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [{
"op": "{add|remove|replace}"
"path": "{scim path to attribute}”
"value": {new value, if applicable}
}]
}
This is somewhat complicated to implement & test, especially for an Application Provider with limited resources.
Therefore, the following is a proposal for simplification: Only support replacement in full. Each update must resend the full User object, even if only 1 value has changed.
Some IdPs already behave this way. Others don’t.
While it feels inefficient, I suspect in practice it makes little difference. For example, a common implementation is to store User objects as JSON blobs in a datastore. All modifications require reading the full object and persisting back, regardless of how much is being updated.
Another consideration of this approach is when an Application Provider wishes to know which attributes have been modified. For this purpose, they must examine the deltas themselves. I would argue the analysis of deltas is always necessary regardless of the update method, since it’s possible for a SCIM client to send a “replace” request for a single attribute but replace it with the same value that already exists, essentially resulting in a no-op. The Application Provider always bears the burden of calculating what has truly changed.
I am interested in working group feedback here. Do scenarios exist where this doesn’t succeed? My suspicion is this works for the vast majority of Applications, and if we need more complexity, we should make that an optional step-up.
Example:
PUT /Users/{id} HTTP/1.1
Host: example.com
Accept: application/scim+json
Content-Type: application/scim+json
Authorization: Bearer h480djs93hd8
Content-Length: ...
{
"schemas":["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName":"bjensen",
"externalId":"bjensen",
"name":{
"formatted":"Ms. Barbara J Jensen III",
"familyName":"Jensen",
"givenName":"Barbara"
}
}
Delete User
SCIM Operation: DELETE /Users/{id}
Example:
DELETE /Users/{id}
Host: example.com
Authorization: Bearer h480djs93hd8
Get User Details
Question for the Working Group: Does anyone need the ability to retrieve a User by UserId in order to accomplish SCIM provisioning? If so, the following mechanism is proposed:
SCIM Operation: GET /Users/{id}
Example:
GET /Users/{id}
Host: example.com
Accept: application/scim+json
Authorization: Bearer h480djs93hd8
The “groups” attribute on the User object must be undefined. This can be made explicit by requiring that callers provide the “excludedAttributes” parameter.
Example w/ excludedAttributes
GET /Users/{id}?excludedAttributes=groups
Host: example.com
Accept: application/scim+json
Authorization: Bearer h480djs93hd8
Search Users
The SCIM Service must support searching based on the following filters:
* “username eq”
* “externalId eq” (If the IdentityProvider has specified a value for externalId)
The SCIM Client may use these APIs to translate a foreign key, such as username and externalId, into the primary id of the SCIM Service.
The response only needs to include the id attribute. This is formalized by specifying the attribute in the query parameters.
SCIM Operation: GET /Users?attributes+eq+id&filter=...
Example:
GET /Users?attributes+eq+id&filter=userName+eq+{username}
Host: example.com
Accept: application/scim+json
Authorization: Bearer h480djs93hd8
HTTP/1.1 200 OK
Content-Type: application/scim+json
{
"schemas":["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
"totalResults":1,
"Resources":[
{
"id":"2819c223-7f76-453a-919d-413861904646"
}
]
}
All Other Operations
Other operations are out of scope for SCIM provisioning between an Identity Provider and Application Provider.
Applications may support additional functionality, but it is not required for FastFed Compatibility.
Group Operations
________________________________
Create Group
SCIM Operation: POST /Groups
Example:
POST /Groups HTTP/1.1
Host: example.com
Accept: application/scim+json
Content-Type: application/scim+json
Authorization: Bearer h480djs93hd8
Content-Length: ...
{
"schemas": ["urn:scim:schemas:core:1.0"],
"externalId": "e5a41517-bcd6-4b8b-8590-487ae996de44",
"displayName": "Group Name"
}
Notes:
* DisplayName is required
* ExternalId is optional
* Members must be empty/undefined. (Technical note: The reasoning here is two-fold. First, some implementations can store group metadata separately from group memberships, making it difficult to adhere to the atomic update requirements for POST and PATCH operations on a two-phase commit. Second, even if that’s not the case, SCIM doesn’t provide any guidance (or way to communicate) regarding the maximum number of membership changes in a single POST/PATCH, such that even those with transactional capabilities may only be capable of performing a limited quantity of work within a transactional unit. See later sections for recommended handling of group membership.)
Update Group Metadata
SCIM Operation: PATCH /Groups/{id}
Example:
PATCH /Groups/{id} HTTP/1.1
Host: example.com
Accept: application/scim+json
Content-Type: application/scim+json
Authorization: Bearer h480djs93hd8
Content-Length: ...
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [
{
"op": "replace",
"path": "externalId",
"value": "{newExternalId}"
},
{
"op": "replace",
"path": "displayName",
"value": "{newDisplayName}"
}
]
}
Notes:
* Operation is always “replace”.
* ExternalId and DisplayName cannot be replaced with empty/null values.
* Membership changes cannot be included when updating group metadata. (For same reasoning as in Create Group.)
Delete Group
SCIM Operation: DELETE /Groups/{id}
Example:
DELETE /Groups/{id} HTTP/1.1
Host: example.com
Accept: application/scim+json
Content-Type: application/scim+json
Authorization: Bearer h480djs93hd8
Add User to Group
SCIM Operation: PATCH /Groups/{id}
Example:
PATCH /Groups/{id} HTTP/1.1
Host: example.com
Accept: application/scim+json
Content-Type: application/scim+json
Authorization: Bearer h480djs93hd8
Content-Length: ...
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [
{
"op": "add",
"path": "members",
"value": [{"id": "{id}"}]
}
]
}
Notes:
* Operation is always “add”.
* Path must be “members”
* Only 1 member can updated per PATCH request (Technical Note: The reasoning here is that SCIM specifies PATCH operations as atomic, but Application Providers should not be required to support an unbounded number of mutations in a single transactional unit. For example, Okta currently sends batches of up to 1000 updates in a single PATCH, which is nearly impossible to handle quickly in a single transaction. In addition, SCIM has no mechanism to communicate partial success/failure of a PATCH request, so even if we tried to mimic a bulk atomic update, sporadic failures will inevitably lead to data drift between the Providers. Much simpler to mutate records 1x1 and parallelize requests as needed to accelerate the overall pace of synchronization.)
* Value is represented as a list with a single element
* The element must contain the attribute “id” representing the identifier of the resource being added.
* The resource type can be a User or Group object (Technical Note: This implies support for nested groups. Is that OK? Do any SaaS Applications really support nested groups? Do they need to block circular memberships, such as a group containing itself?)
* Multiple concurrent PATCH requests can be made for a single group. Maximum concurrency is 100 TPS per group.
Remove User From Group
SCIM Operation: PATCH /Groups/{id}
Example:
PATCH /Groups/{id} HTTP/1.1
Host: example.com
Accept: application/scim+json
Content-Type: application/scim+json
Authorization: Bearer h480djs93hd8
Content-Length: ...
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [
{
"op": "remove",
"path": "members",
"value": [{"id": "{id}"}]
}
]
}
Notes:
* Operation is always “remove”.
* Path must be “members”
* Only 1 member can updated per PATCH request (for all the reasons described in “Add Users To Group”)
* Value is represented as a list with a single element
* The element must contain the attribute “id” representing the identifier of the resource being added.
* Multiple concurrent PATCH requests can be made for a single group. Maximum concurrency is 100 TPS per group.
List Users in Group
Question for the Working Group: Does anyone need this capability for SCIM provisioning, such as for anti-entropy purposes? We haven’t found anyone that uses it.
If unnecessary, that’s fine. Can skip it.
Alternatively, if needed, the following implementation is proposed:
SCIM Operation: GET /Users?attributes+eq+id& filter=groups.value+eq+{groupId}&startIndex=1&count=100
Example:
GET /Users?attributes+eq+id& filter=groups.value+eq+{groupId}&startIndex=1&count=100
Host: example.com
Accept: application/scim+json
Authorization: Bearer h480djs93hd8
HTTP/1.1 200 OK
Content-Type: application/scim+json
{
"schemas":["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
"totalResults":100,
"Resources":[
{
"id":"2819c223-7f76-453a-919d-413861904646"
},
{
"id":"c75ad752-64ae-4823-840d-ffa80929976c"
}
... truncated ...
]
}
Notes:
* Maximum page size is 100
List Group Memberships for User
Question for the Working Group: Same as above, does anyone need this capability for SCIM provisioning. We haven’t found anyone that uses it.
If needed, the following implementation is proposed:
SCIM Operation: GET /Groups?attributes+eq+id& filter=members.value+eq+{userId}&startIndex=1&count=100
Example:
GET /Groups?attributes+eq+id& filter=members.value+eq+{userId}&startIndex=1&count=100
Host: example.com
Accept: application/scim+json
Authorization: Bearer h480djs93hd8
HTTP/1.1 200 OK
Content-Type: application/scim+json
{
"schemas":["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
"totalResults":100,
"Resources":[
{
"id":" a6f93476-854f-43a4-b8e9-ddf8e81c4ca3",
},
{
"id":" 3f946a76-0378-4edd-be50-ff8d118d44fe",
}
... truncated ...
]
}
Other Considerations
________________________________
ETags/If-Match
* SCIM allows this. Does anyone require it for conditional updates?
Service Provider Configuration Endpoints
* SCIM defines endpoints for:
* /ServiceProviderConfig
* /Schemas
* /ResourceTypes
* Anyone require these?
* If so, would it still be required if the Application is known to be FastFed Compatible, as defined in this document?
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.openid.net/pipermail/openid-specs-fastfed/attachments/20200212/7676700d/attachment-0001.html>
More information about the Openid-specs-fastfed
mailing list