Skip to content

[FEATURE]: Entra group-overage support - app-only Graph client + resolution cache #5977

Description

@jonpspri

Epic: #5885 | Added: 2026-07-29 per ultimate-user feedback

Depends on: #5895 (Epic 1 gate). Blocks: #5903.

What

Beyond ~200 group memberships, Entra omits the groups claim and emits overage markers (_claim_names/hasgroups/groups:srcN), which would otherwise look like "user in no groups." The SSO browser flow already solves this with a Graph fallback (sso_service.py:331-462); this story brings equivalent support to the trust-mode request path:

  • Extract the overage-marker detection (sso_service.py:1444-1453) into a helper shared by the SSO enrichment path (behavior unchanged) and the trust-mode claims module.
  • App-only Microsoft Graph client for trust mode: client-credentials token acquisition reusing the SSO provider record's stored (encrypted) client credentials, then POST /users/{oid}/getMemberObjects (securityEnabledOnly: true), bounded by timeout and max-groups settings (generalizing sso_entra_graph_api_*).
  • oid-keyed group-resolution cache (Redis when available; namespaced per the auth-cache work in Epic 1; TTL bounded by the presenting token's exp) — the SSO path calls Graph once per login, but trust mode runs per request, so caching is mandatory.
  • Policy wiring per jwt_trust_overage_policy: graph_lookup → resolve via client (cached); fail_closed (default) → 401 with actionable error; proceed_without_groups → continue with empty groups + warning (parity with current SSO behavior).

Must NOT

  • Never use the inbound bearer token for Graph calls — it is audience-bound to ContextForge and Graph will reject it.
  • No unbounded lookups; no changes to default-mode or SSO browser-flow behavior.

Acceptance criteria

  • uv run pytest tests -k "overage or graph" -q green, including the three-policy matrix and cache assertions (second identical request served from cache — single Graph call).
  • Existing SSO enrichment tests pass unmodified (shared-helper refactor is behavior-identical).
  • Graph acquisition failure under graph_lookup → 401 (fail-closed); under proceed_without_groups → documented degraded behavior.
  • AC-extra-1 (cache-error semantics): When Redis raises an error during cache lookup and the policy is graph_lookup, the implementation MUST treat the error as a cache miss and attempt a live Graph call (not an immediate 401). If the Graph call also fails, 401 is returned. Tests must assert: Redis mock raises ConnectionError + Graph mock succeeds → request authorised; Redis mock raises ConnectionError + Graph mock fails → 401.
  • AC-extra-2 (proceed_without_groups warning log): When jwt_trust_overage_policy = proceed_without_groups, a WARNING-level structured log entry MUST be emitted on every overage-triggered request, carrying the user's oid and a note that group resolution was skipped. This must be asserted in the test matrix row for this policy.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestsecurityImproves security

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions