Authenticate flex-auth calls with a rotating caller token

Closes the caller side of FLEX-WP-0015. FlexAuthHTTPAdapter reads the
audience-scoped projected ServiceAccount token from a file on every decision,
so hourly rotation needs no restart, and runtime configuration now requires
USER_ENGINE_FLEX_AUTH_TOKEN_FILE.

A missing, empty, or unreadable token file fails closed as a denial without
reaching flex-auth: OSError joins the caught set and an empty read raises.
Coverage proves all three unusable-token cases deny before any request is
made, and that neither the deny reason nor the decision repr carries the
token value.

Tenant-authority reads now identify user-engine as actor `user-engine` under
the protected tenant.read action, keeping tenant ids opaque and URL-encoded.

Contract: docs/flex-auth-caller-identity.md. Full suite: 148 tests, 3
provider-gated skips.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
tegwick 2026-08-18 10:53:31 +02:00
parent 6f6bbf5e4a
commit 4622b64061
7 changed files with 174 additions and 8 deletions

View file

@ -42,6 +42,7 @@ def create_application() -> PortalApplication:
authorization=FlexAuthHTTPAdapter(
base_url=_required("USER_ENGINE_FLEX_AUTH_URL"),
timeout_seconds=float(os.environ.get("USER_ENGINE_FLEX_AUTH_TIMEOUT", "3")),
bearer_token_file=_required("USER_ENGINE_FLEX_AUTH_TOKEN_FILE"),
),
)
tenant_management = None