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>
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 payloadX-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