Changelog#
[0.8.0] - Unreleased#
Added#
Cursor pagination of RFC 9865. A configuration that announces it in
paginationgetsnextCursorandpreviousCursorin the search responses. The clients cannot read or forge the cursors. The server refuses a cursor of another query, anothercount, or older thancursorTimeout. The cursors give stable pages: a resource that exists during the whole paging is returned once, even when others are created or deleted. Cursor pagination needs the newsecretparameter ofScimService, thecursorextra, and a storage that sets the newsupports_cursors, such asInMemoryStorage. The server refuses to start otherwise.The
scim2-servercommand pages with cursors when its--service-provider-configannounces them. Its container image includes thecursorextra.WSGIApplicationandASGIApplicationgive their default service the secret of theSCIM2_SERVER_SECRETenvironment variable.The
maxPageSizeofpaginationbounds thecountof every search.ScimStorageContractandAsyncScimStorageContractcheck that the cursors of the storages that support them give stable pages.
Changed#
ScimStorage.searchreturns aSearchPageinstead of a tuple, and takes thepositionof the page when a cursor pages the resources. This also applies to code that calls it directly, such as thesearchmethod ofInMemoryStorage.InMemoryStoragereturns the resources in the order of their identifiers when the search has nosortBy, instead of the order of creation. The same goes for the resources with the same sort value.For the subclasses of
ScimService:read_search(),read_search_queryandread_search_bodyreturn the search request with the position of the page its cursor asks for, andsearch_response()takes the searched resource types and aSearchPageinstead of the number of resources and the page.The WSGI and ASGI applications keep the query parameters without a value, as scim2-flask does. An empty
filter,sortByorcountgets a 400 response instead of being ignored.With
paginationin the configuration, a search withoutcountgetsdefaultPageSizeresources, by index as by cursor, instead of every resource up tomaxResults.A search with a
cursorgets a 501 response when the configuration does not announce cursor pagination, instead of ignoring the cursor.
Deprecated#
A storage whose
searchmethod does not acceptpositionraises aDeprecationWarning. Such a storage pages by index only. scim2-server 0.9 will requireposition.
[0.7.1] - 2026-10-07#
Added#
ScimStorage.getreceives theattributesandexcludedAttributesof a GET request inresponse_parameters. A storage can skip loading the attributes the response leaves out, such as the members of a group.
Changed#
Requires scim2-models 0.12.1.
Deprecated#
A storage whose
getmethod does not acceptresponse_parametersraises aDeprecationWarning. scim2-server 0.9 will require it.
[0.7.0] - 2026-10-06#
Added#
The
--precisoption of thescim2-servercommand compares the usernames and the passwords with the PRECIS profiles of RFC 8265, and refuses the values they do not allow. It needs theprecisextra.
Changed#
Requires scim2-models 0.12. Responses leave out empty values, and list in
schemasonly the extensions they hold. A bulk request without operations answers 400.Filters, sorting and uniqueness checks compare strings as the
comparison_keyof theScimPolicyof the provider says. By default, the case of strings is mapped to lowercase rather than folded:Straßeno longer matchesSTRASSE.The default service provider configuration declares no authentication scheme. Declare the schemes of the application in the
configof the provider.
Fixed#
The in-memory storage compares unique values as filters do. A
userNamewritten with a combining accent clashes with the sameuserNamewritten with a precomposed one.A stored value that the policy cannot compare no longer fails the next writes of the in-memory storage with a 500. It clashes with no other value.
[0.6.2] - 2026-10-06#
Added#
A storage can return references to other resources relative to the SCIM root, such as
Users/2819c223inmembers.$ref. The server returns them as the URLs of the resources, withreference_location().
[0.6.1] - 2026-10-05#
Changed#
bulk_outcome()takes the base URL.locate_bulk_operation()takes the resource type, androute_bulk_operation()returns it.ScimService.check_bulk_targetis removed.max_body_size()returns 0 instead of raising an exception, for a request that fails whatever its body, such as on an unknown path.
Fixed#
Every failed bulk operation has a location, except a failed POST, which has none. scim2-client no longer rejects these bulk responses.
A bulk operation fails with the status of the same request on its own, such as 404 for an unknown endpoint or 405 for
DELETE /Users, instead of 400.A request body without
Content-Typeis read as JSON, instead of answering 415.Each response carrying a resource has a
Content-Locationheader withmeta.location.A provider without
configannounces PATCH, and no other capability. It announced every capability, even those its storage does not implement. A filter or a sort now answers 501, until theconfigannounces it.
[0.6.0] - 2026-10-05#
Added#
authorize()accepts or refuses each operation of a client, bulk operations included.
Changed#
run_bulk_step()takes the bulk request instead of its base URL.The storage contract accepts a version that only changes with the content of the resource.
[0.5.0] - 2026-10-05#
scim2-server becomes a library to build SCIM servers upon, and no longer depends on Werkzeug.
Added#
A core independent of any web framework.
ScimHandlerserves aScimRequestand returns aScimResponse.ScimServiceholds the SCIM rules, and can be subclassed to change one of them, such as the URL of the resources.AsyncScimHandlerserves the same requests in an asynchronous application.ScimStorageandAsyncScimStorage, the interface to keep the resources in any database, withInMemoryStorageandAsyncInMemoryStorage.ScimStorageContractandAsyncScimStorageContractcheck that a storage follows the interface, with thetestingextra.WSGIApplicationandASGIApplicationserve a storage over HTTP, with no other dependency. Their hooks take aScimRequestand return aScimResponse:dispatch_request,check_auth(),get_subject(),handle_exception()andfinalize_response().ForwardedHeaders, a WSGI middleware that builds the URLs of the resources from theX-Forwarded-*headers of a reverse proxy.ROUTESlists the routes of RFC 7644 §3.2, for the integrations that register the routes of their framework./Meserves the resource of the authenticated client, onceme_target()is overridden.A 401 response carries a
WWW-Authenticateheader built from the authentication schemes of the service provider configuration.A request body that is not JSON answers 415.
This documentation.
Changed#
Werkzeug is no longer a dependency. The
scim2-servercommand serves its requests with the WSGI server of the standard library.--debuglogs the WSGI environment of each request, without the debugger and the reloader of Werkzeug.scim2_server.provider.SCIMApplicationbecomesscim2_server.applications.wsgi.WSGIApplication, and takes a storage instead of a backend.scim2_server.tenants.TenantDispatcherbecomesscim2_server.applications.wsgi.TenantDispatcher. Its factory now decides which tenants exist, and returnsNonefor an unknown tenant: thetenantsanddynamicparameters are removed.A concurrent write of a resource between its read and its write answers 412, instead of overwriting the other write. Two writes within the same microsecond get distinct versions.
A method that an endpoint does not support answers 405 with its
Allowheader.HEADis no longer accepted.The
meta.locationof the discovery resources starts with/v2, like the location of the other resources, even for a request without this prefix.An exception other than a
SCIMException, such as a PydanticValidationErrorraised by a storage, answers 500.
Removed#
scim2_server.backend.BackendandInMemoryBackend. Use a storage instead. A storage no longer fillsmeta.location: the server does.SCIMApplication.register_bearer_token. The--bearer-tokenoption of thescim2-servercommand still accepts static bearer tokens. In Python, overridecheck_auth().SCIMApplication.error_from,SCIMApplication.make_errorand thecall_*methods.
Fixed#
A PATCH operation that misses a member RFC 7644 §3.5.2 requires, such as a
removewithoutpathor anaddwithoutvalue, answers 400 and leaves the resource unchanged.A bulk request with a long chain of
bulkIdreferences no longer exhausts the Python stack.The errors of the clients, such as a 404, are logged at the INFO level without traceback. Only the unexpected errors keep their traceback.
[0.4.0] - 2026-10-01#
Added#
Multi-tenancy: the
--tenantoption of thescim2-servercommand serves one set of resources per URL prefix, such as/a/v2/Users. With--dynamic-tenants, the first request to an unknown tenant creates it.
Fixed#
The location of the resources keeps the prefix the application is mounted under.
[0.3.3] - 2026-10-01#
Fixed#
An unsupported method on a discovery endpoint answers 405, and every 405 response lists the supported methods in its
Allowheader.
[0.3.2] - 2026-10-01#
Added#
The
scim2-servercommand serves several requests at once.The package ships its type hints.
Fixed#
A bulk request larger than
maxPayloadSizeis refused before it is read whole.
[0.3.1] - 2026-09-30#
Fixed#
A PUT that changes nothing keeps the
ETagof the resource.
[0.3.0] - 2026-09-28#
Python 3.10 is no longer supported.
Added#
Bulk requests, with
bulkIdreferences between operations andfailOnErrors.The service is described with a
ScimProvider. Thescim2-servercommand takes a--service-provider-configfile, and serves the configuration it describes.Features that the configuration does not support, such as PATCH, sorting or filtering, answer 501. Searches return at most
filter.maxResultsresources. Resources carry versions only when the configuration supports ETags.The payloads are read under the
ScimPolicyof the provider. By default, an attribute that no schema declares answers 400.
Changed#
SCIMProvideris renamedSCIMApplication, to avoid any confusion with theScimProviderof scim2-models.The backend takes the provider, and only stores the resources. Its methods take a
ResourceTypeinstead of its id. Its methods to register and read schemas and resource types are removed.PATCH requests and filters are applied by scim2-models. The PATCH operators and the filter evaluation of scim2-server are removed.
Fixed#
A PATCH that changes nothing keeps the
ETagandlastModifiedof the resource.Conditional headers are evaluated in the order of RFC 7232.
Uniqueness is checked among the resources that share a schema, whatever their resource type.
totalResultscounts every matching resource, anditemsPerPagecounts the returned ones.Validation errors carry a
scimType.An internal error no longer discloses its traceback to the client.
A search request body that is not a JSON object answers 400.
The default schemas follow the RFC errata: the password is case-exact, and the addresses of a user have a
primaryattribute. The enterprise user extension is optional in the default resource types.
[0.2.0] - 2026-09-25#
Added#
A container image is published on the GitHub container registry for each release.
The
--debugoption of thescim2-servercommand.
[0.1.9] - 2026-03-27#
Fixed#
PATCH requests on multi-valued attributes.
[0.1.8] - 2026-01-25#
Python 3.14 is supported.
Fixed#
A PATCH on the root of an extension no longer answers
invalidPath.
[0.1.7] - 2025-07-25#
Fixed#
A PUT that adds an extension to a resource without one no longer fails.
[0.1.6] - 2025-07-23#
Fixed#
Compatibility with scim2-models 0.4.
[0.1.5] - 2025-03-28#
Fixed#
A Pydantic warning.
[0.1.4] - 2025-01-27#
No change for the users.
[0.1.3] - 2025-01-21#
No change for the users.
[0.1.2] - 2024-11-07#
Python 3.10 and 3.13 are supported.
Fixed#
The location of a resource created with POST or replaced with PUT.
[0.1.1] - 2024-09-22#
Added#
The
/v2prefix of the endpoints is optional.
Fixed#
meta.resourceTypeholds the name of the resource type, not its id.
[0.1.0] - 2024-08-30#
Added#
Initial release: an in-memory SCIM server, with the discovery endpoints, the creation, read, replacement, PATCH and deletion of resources, searches with filters and sorting, ETags and uniqueness constraints.