6.8 KiB
Gateway · Identity Header Policy for Router Dispatch
Status
- Implemented in Sprint 8100.0011.0002.
- Middleware:
src/Gateway/StellaOps.Gateway.WebService/Middleware/IdentityHeaderPolicyMiddleware.cs - Last updated: 2025-12-24 (UTC).
Why This Exists
The Gateway is the single HTTP ingress point and routes requests to internal microservices over Router transports. Many services (and legacy components) still rely on header-based identity context (tenant/scopes/actor) rather than (or in addition to) HttpContext.User claims.
This creates a hard security requirement:
- Clients must never be able to inject/override “roles/scopes” headers that the downstream service trusts.
- The Gateway must derive the effective identity from the validated JWT/JWK token (or explicit anonymous identity) and overwrite downstream identity headers accordingly.
Implementation
The IdentityHeaderPolicyMiddleware (introduced in Sprint 8100.0011.0002) replaces the legacy middleware:
(retired)src/Gateway/StellaOps.Gateway.WebService/Middleware/ClaimsPropagationMiddleware.cs(retired)src/Gateway/StellaOps.Gateway.WebService/Middleware/TenantMiddleware.cs
Resolved issues
- Spoofing risk: ✅ Fixed. Middleware uses "strip-and-overwrite" semantics—reserved headers are stripped before claims are extracted and downstream headers are written.
- Claim type mismatch: ✅ Fixed. Middleware uses
StellaOpsClaimTypes.Tenant(stellaops:tenant) with fallback to legacytidclaim. - Scope claim mismatch: ✅ Fixed. Middleware extracts scopes from both
scp(individual claims) andscope(space-separated) claims. - Docs alignment: ✅ Reconciled in this sprint.
Policy Goals
- No client-controlled identity headers: the Gateway rejects or strips identity headers coming from external clients.
- Gateway-controlled propagation: the Gateway sets downstream identity headers based on validated token claims or a defined anonymous identity.
- Compatibility bridge: support both
X-Stella-*andX-StellaOps-*header naming during migration. - Determinism: header values are canonicalized (whitespace, ordering) and do not vary across equivalent requests.
Reserved Headers (Draft)
The following headers are considered reserved identity context and must not be trusted from external clients:
- Tenant / project:
X-StellaOps-Tenant,X-Stella-TenantX-StellaOps-Project,X-Stella-Project
- Scopes / roles:
X-StellaOps-Scopes,X-Stella-Scopes
- Actor / subject (if used for auditing):
X-StellaOps-Actor,X-Stella-Actor
- Token proof / confirmation (if propagated):
cnf,cnf.jkt
Internal/legacy pass-through keys to also treat as reserved:
sub,scope,scp,tid(legacy),stellaops:tenant(if ever used as a header key)
Overwrite Rules (Draft)
For non-system paths (i.e., requests that will be routed to microservices):
- Strip all reserved identity headers from the incoming request.
- Compute effective identity from the authenticated principal:
subfrom JWTsub(StellaOpsClaimTypes.Subject)tenantfromstellaops:tenant(StellaOpsClaimTypes.Tenant)projectfromstellaops:project(StellaOpsClaimTypes.Project) when presentscopesfrom:scpclaims (StellaOpsClaimTypes.ScopeItem) if present, else- split
scope(StellaOpsClaimTypes.Scope) by spaces
- Write downstream headers (compat mode):
- Tenant:
X-StellaOps-Tenant: <tenant>X-Stella-Tenant: <tenant>(optional during migration)
- Project (optional):
X-StellaOps-Project: <project>X-Stella-Project: <project>(optional during migration)
- Scopes:
X-StellaOps-Scopes: <space-delimited scopes>X-Stella-Scopes: <space-delimited scopes>(optional during migration)
- Actor:
X-StellaOps-Actor: <sub>(unless another canonical actor claim is defined)
- Tenant:
Anonymous mode
If Gateway:Auth:AllowAnonymous=true and the request is unauthenticated:
- Set an explicit anonymous identity so downstream services never interpret “missing header” as privileged:
X-StellaOps-Actor: anonymousX-StellaOps-Scopes:(empty) oranonymous(choose one and document)- Tenant behavior must be explicitly defined:
- either reject routed requests without tenant context, or
- require a tenant header even in anonymous mode and treat it as untrusted input that only selects a tenancy partition (not privileges).
Scope Override Header (Offline/Pre-prod)
Some legacy flows allow setting scopes via headers (for offline kits or pre-prod bundles).
Draft enforcement:
- Default: forbid client-provided scope headers (
X-StellaOps-Scopes,X-Stella-Scopes) and return 403 (deterministic error code). - Optional controlled override: allow only when
Gateway:Auth:AllowScopeHeader=true, and only for explicitly allowed environments (offline/pre-prod). - Even when allowed, the override must not silently escalate a request beyond what the token allows unless the request is explicitly unauthenticated and the environment is configured for offline operation.
Implementation Details
Middleware Registration
The middleware is registered in Program.cs after authentication:
app.UseAuthentication();
app.UseMiddleware<SenderConstraintMiddleware>();
app.UseMiddleware<IdentityHeaderPolicyMiddleware>();
Configuration
Options are configured via GatewayOptions.Auth:
Gateway:
Auth:
EnableLegacyHeaders: true # Write X-Stella-* in addition to X-StellaOps-*
AllowScopeHeader: false # Forbid client scope headers (default)
HttpContext.Items Keys
The middleware stores normalized identity in HttpContext.Items using GatewayContextKeys:
Gateway.TenantId— extracted tenant identifierGateway.ProjectId— extracted project identifier (optional)Gateway.Actor— subject/actor from claims or "anonymous"Gateway.Scopes—HashSet<string>of scopesGateway.IsAnonymous—boolindicating anonymous requestGateway.DpopThumbprint— JKT from cnf claim (if present)Gateway.CnfJson— raw cnf claim JSON (if present)
Tests
Located in src/Gateway/__Tests/StellaOps.Gateway.WebService.Tests/Middleware/IdentityHeaderPolicyMiddlewareTests.cs:
- ✅ Spoofed identity headers are stripped and overwritten
- ✅ Claim type mapping uses
StellaOpsClaimTypes.*correctly - ✅ Anonymous requests receive explicit
anonymousidentity - ✅ Legacy headers are written when
EnableLegacyHeaders=true - ✅ Scopes are sorted deterministically
Related Documents
- Gateway architecture:
docs/modules/gateway/architecture.md - Tenant auth contract (Web V):
docs/api/gateway/tenant-auth.md - Router ASP.NET bridge:
docs/modules/router/aspnet-endpoint-bridge.md