scim-patch
TypeScript library that validates and applies RFC 7644 SCIM PATCH operations to user and group resources.
Repository Health
Technical Analysis
scim-patch implements the “Modifying with PATCH” section (3.5.2) of RFC 7644, the SCIM 2.0 protocol used by identity providers like Azure AD, Okta, and OneLogin to provision and update users and groups. Rather than hand-rolling path parsing and array-filter logic for every SCIM integration, it gives you two entry points: patchBodyValidation to check that an incoming PATCH request body is well-formed, and scimPatch to apply a list of add/remove/replace operations directly onto a SCIM resource object.
The library resolves dotted and schema-prefixed attribute paths (including extension schema URIs), supports SCIM’s array filter syntax (e.g. emails[type eq "work"].value) via the scim2-parse-filter package, and mirrors real-world provider quirks such as accepting capitalized operation names (Add, Remove, Replace) for AzureAD compatibility and treating a replace on a missing attribute as an add by default. It also explicitly guards against prototype-pollution paths (__proto__, constructor, prototype) in both the top-level path resolver and the nested assign helper, with dedicated tests for each.
Because identity provisioning is the actual target use case, the library is a thin, dependency-light layer (just fast-deep-equal and scim2-parse-filter) meant to sit inside a larger SCIM server implementation — you bring your own ScimResource-shaped objects and HTTP layer, and scim-patch handles just the PATCH semantics.
What You Get
patchBodyValidationto validate an incoming SCIM PATCH request body against the RFC’s schema and operation rules, throwing typed errors on malformed inputscimPatchto apply an array of add/remove/replace operations to a SCIM resource, withmutateDocumentandtreatMissingAsAddoptions to control in-place mutation and missing-attribute behavior- Support for SCIM’s multi-valued attribute filter syntax (e.g.
addresses[type eq "work"].country) backed by thescim2-parse-filtergrammar - Compatibility handling for provider-specific quirks, including AzureAD’s capitalized operation names (
Add,Remove,Replace) and one-login’s array-shaped remove values - Built-in prototype-pollution protection that rejects
__proto__,constructor, andprototypesegments in patch paths - A typed error hierarchy (
InvalidScimPatchRequest,NoTarget,NoPathInScimPatchOp, and more) that maps cleanly onto SCIM’sscimTypeerror codes for HTTP error responses
Common Use Cases
- Implementing the PATCH endpoint of a self-hosted SCIM 2.0 provisioning server for user/group lifecycle management
- Handling PATCH requests sent by enterprise identity providers such as Azure AD, Okta, or OneLogin during automated user provisioning
- Validating inbound SCIM PATCH payloads before they reach business logic, rejecting malformed operations with RFC-compliant error codes
- Applying incremental updates (e.g. deactivating a user, adding an email, removing a role) to an in-memory or database-backed SCIM resource without diffing full objects
Under The Hood
Architecture
The library exposes two public functions from src/scimPatch.ts: patchBodyValidation, which walks a PATCH request body and validates each operation via validatePatchOperation, and scimPatch, which reduces an array of operations over a SCIM resource using a navigate/extractArray/addOrReplaceAttribute pipeline. Attribute paths are split on unescaped periods and, when a path contains a schema URI prefix (distinguished from core User/Group schemas), the URI is folded back into the path segments to address extension attributes. Every resolved path segment is checked against a DANGEROUS_KEYS set (__proto__, constructor, prototype) before use, and object traversal in both navigate and assign uses Object.prototype.hasOwnProperty.call rather than in or truthy checks specifically to avoid resolving to inherited built-ins — a defense against two named prototype-pollution CVEs (GHSA-9m6g-wc8r-q59c and GHSA-2mhw-wcx5-v3xj) that the test suite in prototypePollution.test.ts exercises directly. Multi-valued attribute filters are delegated to the external scim2-parse-filter grammar, keeping the SCIM filter language itself out of this codebase.
Tech Stack
Written in strict-mode TypeScript (tsconfig.json sets strict, noImplicitReturns, noUnusedLocals) targeting ES2017/CommonJS output. Runtime dependencies are minimal: scim2-parse-filter for the SCIM filter grammar and fast-deep-equal for value comparison during remove/dedup operations. The dev toolchain uses tsc for builds, mocha + chai + nyc for testing and coverage, eslint with TypeScript and mocha plugins for linting, and benchmark for a dedicated perf test. CI (GitHub Actions) runs build, lint, test, and coverage-upload-to-Coveralls on every push and pull request; a separate release.yml workflow handles publishing.
Code Quality
The test suite is extensive relative to the implementation size — scimPatch.test.ts alone runs over a thousand lines covering add/remove/replace across scalar, nested, and multi-valued-with-filter paths, patchValidation.test.ts covers request-body validation edge cases, and a dedicated prototypePollution.test.ts file specifically targets the two named security advisories with regression tests. Errors are modeled as a typed class hierarchy (ScimError → InvalidScimPatch/InvalidScimRemoveValue → specific subclasses like NoTarget, FilterOnEmptyArray) rather than thrown strings, letting callers pattern-match on error type and expose RFC scimType codes. Strict TypeScript, ESLint, and CI enforcement across build/lint/test give reasonable confidence the exported surface behaves as typed.
API Design
The public API is deliberately small — two functions and a set of exported types/errors — which keeps integration low-friction: consumers only need to shape their SCIM resource as a ScimResource-compatible object and call scimPatch(resource, operations, options). Defaults (mutateDocument: true, treatMissingAsAdd: true) match the most common self-hosted-provisioning behavior, and the two options that do exist map directly onto documented SCIM ambiguities (mutate-in-place vs. copy, and how to treat a replace against a missing attribute) rather than exposing internal implementation knobs. The README’s example walks through validating a request and then patching a concrete ScimUser type end-to-end, and the exported error classes double as documentation for the RFC’s scimType error codes.
Used by 3 apps in this directory
Budibase
Low Code Platforms · No Code Platforms
Build AI agents, automations, and internal apps on a single open-source platform with full self-hosting control.
Infisical
Security · Devops
The open-source platform for secrets, certificates, privileged access, and AI agent security — all in one self-hostable system.
Lightdash
Analytics · Data Engineering
The open-source Looker alternative that turns your dbt project's metrics and dimensions into governed, self-serve charts and dashboards — no license key required.