Skip to content

feat(platform): accept a host-verified identity in bearer auth - #516

Open
angel-romero-flo wants to merge 2 commits into
nerdsane:mainfrom
angel-romero-flo:host-verified-identity
Open

angel-romero-flo wants to merge 2 commits into
nerdsane:mainfrom
angel-romero-flo:host-verified-identity

Conversation

@angel-romero-flo

@angel-romero-flo angel-romero-flo commented Oct 1, 2026 •

Copy link
Copy Markdown

Problem

A host that embeds the platform router can authenticate callers from credentials the platform cannot interpret (for example, Datadog Internal Service Authentication JWTs validated by the host against rotating keys). The only way to get such a caller into bearer_auth_check today is to mint and register a second credential the platform can resolve (a trusted issuer plus a host-side signing key, or an AgentCredential per caller). A protected request without a resolvable credential gets 401, even when an outer layer already verified the caller.

Change

  • New temper_platform::host_identity::HostVerifiedIdentity(pub SecurityContext): a principal the host verified in its own middleware, in front of the platform router.
  • bearer_auth_check checks for it right after parsing the tenant, before the internal-invocation branch. When present, it removes the extension and the Authorization header, and attaches AuthenticatedRequestContext::new(<requested tenant>, <context>). Session and intent headers become telemetry on that context, as on the credential path for a session nobody verified; neither becomes a Cedar input.
  • Only in-process code can set request extensions; no header or token produces this identity. Since the extension is removed when read, handlers never see it. The identity only authenticates: the requested tenant's Cedar policy still authorizes it, and a tenant with no policy denies every non-System principal. This mirrors how internal invocation capabilities already restore a context without identity resolution.
  • Without the extension, nothing changes.

This branch is based directly on nerdsane/temper:main (1be4b7be003f35bdacfbc24f7f4eb7e77bdade48); the fork only supplies the source branch. The consumer is Datadog's temper-cloud control plane. It validates ISA tokens with ddauthn and attaches the caller as Customer::"<email>", which removes its current token-minting exchange.

Verification

  • New tests in bearer_auth/tests.rs, all through strip_inbound_identity_headers and bearer_auth_check:
    • a host layer inserts Customer::"ada@example.com", and the request also carries an Authorization: Bearer that no credential resolves with X-Tenant-Id: tenant-a: 200, the handler sees tenant-a:Customer:ada@example.com and no Authorization header;
    • the same request without the host layer: 401;
    • a host-identity request with X-Session-Id and X-Intent: both reach the context as telemetry, and the Cedar sessionId stays unset;
    • through build_platform_router, a host identity reading /tdata/AgentTypes in a tenant with no Cedar policy: 403; after loading a policy that permits Customer: 200.
      The first and third tests failed with their code disabled and pass with it. All 25 tests in the module pass, including the existing ones (headers cannot forge identity, tenant-bound credentials, public routes).
  • cargo fmt --check, scripts/readability-ratchet.sh check .ci/readability-baseline.env (blocking metrics OK), and cargo clippy --workspace --all-targets -- -D warnings: clean.
  • cargo test --workspace --no-fail-fast, with a local PostgreSQL in place of testcontainers (TEMPER_ACTOR_TEST_DATABASE_URL): 3525 passed, 0 failed, 12 ignored. In an earlier run of this suite, temper-wasm http_stream_outbound::outbound_streaming_1mib_roundtrip failed intermittently (933888 of 1048576 bytes received). It also fails 1 of 3 runs on main, and temper-wasm does not depend on temper-platform.

RetriggerConfidence Score: 5/5

The PR appears safe to merge; no outstanding findings or new actionable issues remain.

Summary

The PR lets an embedding host supply a verified identity to bearer authentication while retaining requested-tenant policy authorization.

  • The host-identity path removes the bearer header and carries session and intent headers as telemetry.
  • Router-level tests cover authentication, telemetry, and tenant-policy denial and permission.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
    H[Host verifies caller] --> E[HostVerifiedIdentity extension]
    E --> B[Bearer authentication binds requested tenant]
    B --> C[Authenticated request context]
    C --> P[Tenant Cedar policy]
    P -->|Permit| R[Protected route]
    P -->|Deny| F[403]
Loading

Reviews (3) · Last reviewed commit: "fix(platform): keep session and intent t..."

Comment thread crates/temper-platform/src/bearer_auth.rs Outdated
Comment thread crates/temper-platform/src/bearer_auth/tests.rs
A process embedding the platform router can verify a caller from a credential the platform cannot interpret and insert a HostVerifiedIdentity request extension before the router. bearer_auth_check removes the extension, drops the Authorization header, and authenticates the request as that principal in the requested tenant; that tenant's Cedar policy still authorizes it. Request extensions cannot be set over HTTP, so no header produces this identity.
…entities

A request authenticated by a HostVerifiedIdentity carries its X-Session-Id and X-Intent values on the authenticated context as telemetry, as the credential path does for an unverified session; neither becomes a Cedar input. Tests cover that, and that the requested tenant's Cedar policy denies a host-verified identity until a policy permits it.

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant