# 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 ` 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()`: ```csharp 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 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`