# 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`: ```json { "id": "check:", "tenant": "tenant:friendly:binky", "subject": {"id": "", "type": "service"}, "action": "tenant.create", "resource": {"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 **deny** — `FlexAuthCheckClient.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. | ## Related - `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-cape` → `tenant-engine`), not covered by this doc. - `net-kingdom/canon/standards/tenant-engine-boundary-contract_v0.1.md` — the Authorization Contract section this client implements.