Files
git.stella-ops.org/docs/modules/router/IDENTITY_ENVELOPE_MIDDLEWARE.md
master 4d8a48a05f Sprint 7+8: Journey UX fixes + identity envelope shared middleware
Sprint 7 — Deep journey fixes:
  S7-T01: Trust & Signing empty state with "Go to Signing Keys" CTA
  S7-T02: Notifications 3-step setup guide (channel→rule→test)
  S7-T03: Topology validate step skip — "Skip Validation" when API fails,
    with validateSkipped signal matching agentSkipped pattern
  S7-T04: VEX export note on Risk Report tab linking to VEX Ledger

Sprint 8 — Identity envelope shared middleware (ARCHITECTURE):
  S8-T01: New UseIdentityEnvelopeAuthentication() extension in
    StellaOps.Router.AspNet. Reads X-StellaOps-Identity-Envelope headers,
    verifies HMAC-SHA256 via GatewayIdentityEnvelopeCodec, creates
    ClaimsPrincipal with sub/tenant/scopes/roles. 5min clock skew.
  S8-T02: Concelier refactored — removed 78 lines of inline impl,
    now uses shared one-liner
  S8-T03: Scanner — UseIdentityEnvelopeAuthentication() added
  S8-T04: JobEngine — UseIdentityEnvelopeAuthentication() added
  S8-T05: Timeline — UseIdentityEnvelopeAuthentication() added
  S8-T06: Integrations — UseIdentityEnvelopeAuthentication() added
  S8-T07: docs/modules/router/IDENTITY_ENVELOPE_MIDDLEWARE.md

All services now authenticate ReverseProxy requests via gateway envelope.
Scanner scan submit should now work with authenticated identity.

Angular: 0 errors. .NET (6 services): 0 errors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 18:27:46 +02:00

4.0 KiB

Identity Envelope Middleware

What

UseIdentityEnvelopeAuthentication() is a shared ASP.NET Core middleware that reads gateway-signed identity envelope headers and hydrates HttpContext.User with the enclosed claims. It lives in StellaOps.Router.AspNet and is consumed by every downstream microservice that receives proxied requests from the Router gateway.

Why

When the Router gateway forwards a request via YARP ReverseProxy, it strips the original Authorization: Bearer <jwt> header. Instead, it signs the authenticated user's identity into two custom headers:

  • X-StellaOps-Identity-Envelope -- Base64URL-encoded JSON payload
  • X-StellaOps-Identity-Envelope-Signature -- HMAC-SHA256 signature

Downstream services need a way to recover the caller's identity from these headers without each service re-implementing the same verification logic. The shared middleware eliminates duplication and ensures consistent claim mapping everywhere.

How to use

In the service's Program.cs, register the middleware before UseAuthentication():

using StellaOps.Router.AspNet;

// ...

app.UseIdentityEnvelopeAuthentication();   // <-- must come first
app.UseAuthentication();
app.UseAuthorization();

The service's .csproj must reference StellaOps.Router.AspNet (which transitively brings in StellaOps.Router.Common containing the codec).

Configuration

The middleware reads the HMAC-SHA256 signing key from one of two sources (first wins):

Source Key
IConfiguration Router:IdentityEnvelopeSigningKey
Environment variable STELLAOPS_IDENTITY_ENVELOPE_SIGNING_KEY

In the docker-compose environment the key is injected via x-router-microservice-defaults so all services share the same value automatically.

Claim mapping

When a valid, non-expired envelope is verified, the middleware creates a ClaimsPrincipal with authentication type StellaRouterEnvelope and the following claims:

Claim type Source
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier envelope.Subject
sub envelope.Subject
stellaops:tenant envelope.Tenant
tenant envelope.Tenant
stellaops:project envelope.Project
project envelope.Project
scope (one per scope) envelope.Scopes
http://schemas.microsoft.com/ws/2008/06/identity/claims/role (one per role) envelope.Roles

Architecture flow

Browser
  |
  |  Authorization: Bearer <jwt>
  v
Gateway (StellaOps.Gateway.WebService)
  |  1. Validates JWT
  |  2. Signs identity into envelope headers (HMAC-SHA256)
  |  3. Strips Authorization header
  |  4. Forwards via YARP ReverseProxy
  v
Downstream Service (Scanner, JobEngine, Timeline, Integrations, Concelier, ...)
  |  UseIdentityEnvelopeAuthentication()
  |  1. Reads X-StellaOps-Identity-Envelope + Signature headers
  |  2. Verifies HMAC-SHA256 signature
  |  3. Checks clock skew (5 min tolerance)
  |  4. Hydrates HttpContext.User
  v
  UseAuthentication() / UseAuthorization()
  (standard ASP.NET pipeline continues with the hydrated principal)

Error handling

The middleware never throws. All failures (missing headers, missing key, bad signature, expired envelope) are logged at Warning level under the StellaOps.IdentityEnvelope logger category. The request continues unauthenticated, allowing the standard JWT bearer handler to attempt authentication normally.

Adopted services

Service File
Concelier src/Concelier/StellaOps.Concelier.WebService/Program.cs
Scanner src/Scanner/StellaOps.Scanner.WebService/Program.cs
JobEngine src/JobEngine/StellaOps.JobEngine/StellaOps.JobEngine.WebService/Program.cs
Timeline src/Timeline/StellaOps.Timeline.WebService/Program.cs
Integrations src/Integrations/StellaOps.Integrations.WebService/Program.cs

Source

  • Middleware extension: src/Router/__Libraries/StellaOps.Router.AspNet/IdentityEnvelopeMiddlewareExtensions.cs
  • Envelope model and codec: src/Router/__Libraries/StellaOps.Router.Common/Identity/GatewayIdentityEnvelope.cs