Tessera

← All technologies

TECHNOLOGY

SCIM 2.0

SCIM 2.0

A service provider that passes both Okta CRUD and the Microsoft SCIM Validator — absorbing both Entra PATCH dialects.

SCIM 2.0 (RFC 7642/7643/7644) is the provisioning wire protocol, and the only real test of a SCIM service provider is that both Okta and Microsoft Entra drive it cleanly — and they disagree. Entra sends a capitalized op, can encode active as the string "False", and uses a no-path multi-attribute replace; Okta sends a no-path replace with a boolean. The Tessera SCIM endpoint absorbs all of it with one PATCH engine over a canonical attribute tree.

Two rules prevent the classic failures: active:false is a soft delete (the user stays GET-able), and an unknown-user filter returns a 200 empty ListResponse, never a 404 — which is exactly what Entra’s “Test Connection” probe checks. Everything is served as application/scim+json over TLS 1.2+.

Code

rust
// Absorb both IdP dialects: Entra sends capitalized `op` and (legacy)
// `active` as the STRING "False"; Okta sends a no-path replace. One
// PATCH engine handles add/replace/remove over a canonical attribute tree.
fn normalize_op(op: &str) -> Op { op.to_ascii_lowercase().parse().unwrap() }

fn parse_active(v: &serde_json::Value) -> bool {
    match v {
        serde_json::Value::Bool(b) => *b,
        serde_json::Value::String(s) => !s.eq_ignore_ascii_case("false"),
        _ => true,
    }
}
// active=false is a SOFT delete: the user stays GET-able (never hard-delete).
// Unknown user filter to 200 empty ListResponse (never 404); counts are integers.

Standards it follows

SCIM Definitions, Overview, ConceptsRFC 7642
https://www.rfc-editor.org/rfc/rfc7642

Best practices applied

  • Normalize `op` case-insensitively and accept `active` as a boolean AND the string "False" (Entra legacy dialect). source
  • Handle `replace` both with and without `path`, and group-member removal as both a value array and `members[value eq "..."]`. source
  • Never hard-delete on `active:false` — keep the resource GET-able (soft delete). source
  • Match by `userName` AND `externalId`; a zero-result filter returns a 200 empty ListResponse, never 404; counts are integers. source
  • Serve `application/scim+json` over TLS 1.2+ with a public CA, and statically compile /Schemas, /ResourceTypes, /ServiceProviderConfig. source