tenant-engine/docs/flex-auth-integration.md
tegwick 5d57c7d488 TEN-WP-0003: FlexAuthWriteAuthorizer -- gate writes through flex-auth
flex_auth.py: CheckRequest + FlexAuthCheckClient against flex-auth's real
POST /v1/check contract (schemas/check_request.schema.json,
decision_envelope.schema.json, read directly from the flex-auth repo, not
guessed). Fail-closed by construction: only effect=="allow" authorizes;
every other effect, non-200, malformed body, or transport failure resolves
to deny, nothing raises past is_allowed().

authz.FlexAuthWriteAuthorizer implements the existing WriteAuthorizer
Protocol. Action -> resource-type mapping coordinated with FLEX-WP-0008's
planned vocabulary (both repos reference the same table).
DefaultDenyWriteAuthorizer stays the fallback when no flex-auth URL is
configured.

config.py: Settings.from_env(), mirroring qonto-assistant's pattern.
docs/flex-auth-integration.md documents the contract, fail-closed rule,
and current real state (denies everything until FLEX-WP-0008 lands).

60 tests passing. Verified live twice over real HTTP between separate
processes (not just MockTransport): a deny-returning flex-auth double
produces 403 from POST /tenants, an allow-returning one produces 201.

Also registered (not implemented) the two workplans this depends on for a
complete picture: flex-auth/FLEX-WP-0008 (protected-system registration --
what makes allow reachable) and key-cape/KEY-WP-0005 (discovered key-cape
emits none of iam-profile_v0.3.md's core claims yet, not just missing
tenant_roles -- a bigger, security-sensitive gap flagged rather than
quietly worked around).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 22:54:44 +02:00

3 KiB

flex-auth Integration (TEN-WP-0003)

tenant-engine gates every write through flex-auth's POST /v1/check (flex_auth.FlexAuthCheckClient, wired in as authz.FlexAuthWriteAuthorizer).

What tenant-engine sends

A CheckRequest per flex-auth/schemas/check_request.schema.json:

{
  "id": "check:<uuid>",
  "tenant": "tenant:friendly:binky",
  "subject": {"id": "<actor>", "type": "service"},
  "action": "tenant.create",
  "resource": {"id": "<tenant_id>", "type": "tenant", "system": "tenant-engine"}
}

Action → resource-type mapping (must match flex-auth's FLEX-WP-0008-T01 vocabulary exactly — coordinate values, don't diverge):

Action Resource type
tenant.create tenant
tenant.role.grant role-grant
tenant.role.revoke role-grant
tenant.plan.assign plan-assignment

What tenant-engine expects back

A DecisionEnvelope per flex-auth/schemas/decision_envelope.schema.json. Only effect: "allow" authorizes the write. Every other effect (deny/redact/audit_only/not_applicable), a non-200 response, a malformed body, or a transport failure/timeout all resolve to denyFlexAuthCheckClient.is_allowed() never raises past its own boundary; it always returns a plain bool.

The current real state

Until flex-auth/workplans/FLEX-WP-0008-tenant-engine-consumer-integration.md lands (resource/action vocabulary + an authored policy package), every check resolves to deny or not_applicable — verified against real flex-auth behavior, not assumed. This is correct fail-closed behavior, not a bug: tenant-engine cannot perform any write against a real flex-auth deployment until that policy package exists. Verified locally with a fake HTTP double standing in for flex-auth (deny → 403, allow → 201, both over real HTTP between two processes) — see TEN-WP-0003's closure notes for the exact commands.

Configuration

Env var Default Meaning
TENANT_ENGINE_FLEX_AUTH_URL unset Base URL of a reachable flex-auth deployment. When unset, create_app() falls back to authz.DefaultDenyWriteAuthorizer — no writes ever succeed, which is the correct posture for local/test runs that have no flex-auth to call.
TENANT_ENGINE_FLEX_AUTH_TIMEOUT_SECONDS 3 Bounded timeout on the synchronous write path — no retries, so a slow deny doesn't become a hang.
  • flex-auth/workplans/FLEX-WP-0008-tenant-engine-consumer-integration.md — the flex-auth-side work this client depends on for real allow decisions, and (separately) flex-auth's own live-lookup consumption of tenant-engine's /roles/live endpoint for other protected systems.
  • key-cape/workplans/KEY-WP-0005-iam-profile-core-claims.md — the cache-read/tenant_roles direction (key-capetenant-engine), not covered by this doc.
  • net-kingdom/canon/standards/tenant-engine-boundary-contract_v0.1.md — the Authorization Contract section this client implements.