Overview#

scim2-models validates, serializes, and updates System for Cross-domain Identity Management (SCIM) resources with Pydantic. Use it in a client or server application to represent SCIM payloads and apply SCIM validation rules. It does not provide HTTP endpoints, persistence, authorization, or database query generation.

The SCIM data model and SCIM protocol specifications define the vocabulary used here.

Install scim2-models in the application environment:

pip install scim2-models

This page introduces the operations used most often by SCIM clients and servers. Follow it in order for a first tour. The how-to guides cover focused tasks, the explanations cover protocol behaviour, the integrations cover frameworks, and the Reference lists the complete API.

Create and access a resource#

Create a resource with Python’s snake-case attribute names. Dot notation uses those Python names, while brackets accept SCIM attribute names and paths:

>>> from scim2_models import User
>>> user = User(user_name="bjensen")
>>> user.display_name = "Barbara Jensen"
>>> user["nickName"] = "Babs"
>>> user["name.familyName"] = "Jensen"
>>> user["name.familyName"]
'Jensen'

Remove an attribute with del or assign None:

>>> del user["nickName"]
>>> user.nick_name is None
True

The Access resource values with paths guide shows how to select, change, or remove values through paths.

Validate and serialize SCIM payloads#

Pass the Context matching the HTTP operation while parsing or serializing a payload. The context applies SCIM rules such as ignoring client-supplied, server-managed attributes:

>>> from scim2_models import Context, ResponseParameters, User
>>> payload = {
...     "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
...     "id": "client-supplied",
...     "userName": "bjensen",
... }
>>> user = User.model_validate(payload, scim_ctx=Context.RESOURCE_CREATION_REQUEST)
>>> user.id is None
True

Use the corresponding response context to produce a SCIM response. It also applies an attribute projection requested by a client:

>>> user.id = "2819c223-7f76-453a-919d-413861904646"
>>> user.display_name = "Babs Jensen"
>>> response = user.model_dump(
...     scim_ctx=Context.RESOURCE_QUERY_RESPONSE,
...     response_parameters=ResponseParameters(excluded_attributes=["displayName"]),
... )
>>> response["id"]
'2819c223-7f76-453a-919d-413861904646'
>>> "displayName" in response
False

Validate and serialize SCIM payloads walks through validating creation requests, serializing projected responses, and replacing resources. SCIM contexts and attribute characteristics describes contexts and attribute characteristics. The FastAPI guide shows context type aliases in an endpoint signature.

Read a typed collection#

Parameterize ListResponse with the resource type expected in the Resources collection:

>>> from scim2_models import ListResponse, User
>>> response = ListResponse[User].model_validate(
...     {
...         "totalResults": 1,
...         "Resources": [
...             {
...                 "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
...                 "userName": "bjensen",
...             }
...         ],
...     }
... )
>>> response.resources[0].user_name
'bjensen'

Use a union such as ListResponse[User | Group] when an endpoint returns multiple resource types.

Use a schema extension#

Add a standard or custom extension as a resource type parameter. Access extension values through the extension type:

>>> from scim2_models import EnterpriseUser, User
>>> user = User[EnterpriseUser](user_name="bjensen")
>>> user[EnterpriseUser] = EnterpriseUser(employee_number="701984")
>>> user.model_dump()[
...     "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"
... ]["employeeNumber"]
'701984'

Define custom SCIM models explains how to expose a resource or extension specific to one service. Generate models from SCIM schemas explains how to create a Python model from a schema published by another SCIM server.

Filter resources#

Bind ScimFilter to the resource model before matching it. This validates both the filter syntax and the attributes and operators it uses:

>>> from scim2_models import ScimFilter, User
>>> user = User(user_name="bjensen")
>>> ScimFilter[User]('userName sw "bje"').match(user)
True

Build safe SCIM filters explains how to construct filters safely from dynamic values or application choices. Filter deviations from the SCIM RFC describes where the implemented grammar departs from the RFC.

Replace and patch a resource#

Validate a replacement with RESOURCE_REPLACEMENT_REQUEST, then use replace() with the stored resource. This checks immutable values:

>>> replacement = User.model_validate(
...     {"schemas": payload["schemas"], "userName": "bjensen"},
...     scim_ctx=Context.RESOURCE_REPLACEMENT_REQUEST,
... )
>>> replacement.replace(user)

Apply a PatchOp to make a partial update:

>>> from scim2_models import PatchOp
>>> patch = PatchOp[User].model_validate(
...     {
...         "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
...         "Operations": [{"op": "replace", "path": "displayName", "value": "Babs"}],
...     },
...     scim_ctx=Context.RESOURCE_PATCH_REQUEST,
... )
>>> patch.patch(user)
True
>>> user.display_name
'Babs'

PATCH semantics and interoperability describes what an operation does, what a path selects, and which error a rejected one answers.

Return SCIM errors#

Convert a Pydantic validation error into a SCIM error response before returning it from an HTTP endpoint:

>>> from pydantic import ValidationError
>>> from scim2_models import Error
>>> try:
...     User.model_validate(
...         {"userName": None},
...         scim_ctx=Context.RESOURCE_CREATION_REQUEST,
...     )
... except ValidationError as exc:
...     error = Error.from_validation_error(exc.errors()[0])
>>> error.scim_type
'invalidValue'

Consult the reference for the complete SCIM exception hierarchy.