feat(sso): OIDC/SAML federation + SCIM 2.0 provisioning (Step 6.8) - #159
Merged
Conversation
New rag-sso package delivering enterprise identity in two surfaces:
- Federation: FederatedAuth is an Auth SPI backend over per-tenant
OidcProvider / SamlProvider, so a presented IdP token / assertion is
verified into a Principal at the existing authenticate(token, tenant_id)
boundary with no middleware change. IdP group claims map to acl_labels,
so Step 6.3 ACL push-down + 6.5 PII egress govern federated users.
Dependency-free defaults (stdlib HS256 JWT with full exp/nbf/iss/aud +
constant-time compare; defusedxml SAML validating Issuer/Conditions/
Audience); asymmetric OIDC (PyJWT) + SAML XML-DSig (signxml) behind
[oidc] / [saml] extras; an algorithm allowlist designs out alg:none /
RS<->HS confusion.
- Provisioning: SCIM 2.0 ScimService over a new tenant-scoped ScimStore
SPI (+ NoopScimStore) drives /scim/v2/{Users,Groups} CRUD with the IdP
deactivation PATCH, authenticated by a per-tenant SCIM bearer token
(independent of the JWT Auth backend); SCIM-shaped errors; disabled -> 404.
Per-tenant IdP config on tenants[].sso; cfg.sso / cfg.scim; GET
/v1/status/sso; PII-free sso.* / scim.* events (hashed subject); ragctl
sso / scim demos. Core types FederatedIdentity / ScimUser / ScimGroup +
SsoError/ScimError/ScimNotFoundError/ScimConflictError; dist schemas /
openapi / rag.schema regenerated.
All gates green: ruff, mypy --strict (320 files), RAG001, full pytest
(excl integration), schema/openapi/config-drift, policy-coverage,
log-schema/event-registry/PII.
Docs: docs/reference/sso.md, docs/architecture/sso-scim.md,
docs/adr/ADR-0040-sso-scim.md, docs/README.md. TRACKER: 6.8 done (72/84).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Step 6.8 — SSO / SCIM: OIDC + SAML IdP federation, SCIM 2.0 user provisioning, and per-tenant IdP config, in one PR. New
rag-ssopackage.FederatedAuthis anAuthSPI backend over per-tenantOidcProvider/SamlProvider. The gateway middleware already callsauth.authenticate(token, tenant_id) → Principalat the boundary, so wiring it is the whole integration — no middleware change. IdP group claims →acl_labels, so Step 6.3 ACL push-down + 6.5 PII egress govern federated users unchanged (authorizestays a coarse allow — federation establishes who, the PolicyEngine decides what).exp/nbf/iss/aud+ constant-time compare) anddefusedxmlSAML (Issuer / Conditions / Audience); asymmetric OIDC (PyJWT,[oidc]) + SAML XML-DSig (signxml,[saml], injected verifier → fail-closed). An algorithm allowlist designs outalg:none/ RS↔HS confusion.ScimServiceover a new tenant-scopedScimStoreSPI (+NoopScimStore) drives/scim/v2/{Users,Groups}CRUD incl. the IdP deactivation PATCH, authenticated by a per-tenant SCIM bearer token (independent of the JWTAuthbackend); SCIM-shaped errors (RFC 7644); disabled → 404.tenants[].sso;cfg.sso/cfg.scim;GET /v1/status/sso; PII-freesso.*/scim.*events (subject hashed, never email/userName);ragctl sso/ragctl scimdemos.FederatedIdentity/SsoProtocol/ScimUser/ScimGroup+SsoError/ScimError/ScimNotFoundError/ScimConflictError; wire typesScimListResponse/ScimPatchOp/ScimErrorBody/SsoStatusResponse.dist/schemas+dist/openapi+dist/rag.schemaregenerated.Deferred: remote JWKS discovery + rotation (configured static keys only), SAML SP-initiated redirect + metadata, SCIM bulk /
/Me/ ETag, directory-backed deprovisioning at authenticate-time, an admin-console SSO/SCIM card.Documentation
docs/reference/sso.md— public API + usage + config + eventsdocs/architecture/sso-scim.md— theAuthseam, OIDC/SAML/SCIM internals, security/privacydocs/adr/ADR-0040-sso-scim.md— decision + alternativesdocs/README.mdindex +TRACKER.md(6.8 ✅, 72/84)Test plan
ScimStorecontract suite (CRUD, pagination, tenant isolation)rag-ssounit tests: JWT (expiry/iss/aud/tamper/alg:none/allowlist), OIDC, SAML (conditions/audience/issuer/signature-required), FederatedAuth, ScimServiceragctl sso/ragctl scim🤖 Generated with Claude Code