Files
git.stella-ops.org/docs/modules/gateway/identity-header-policy.md
2025-12-24 12:38:14 +02:00

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:

  • src/Gateway/StellaOps.Gateway.WebService/Middleware/ClaimsPropagationMiddleware.cs (retired)
  • src/Gateway/StellaOps.Gateway.WebService/Middleware/TenantMiddleware.cs (retired)

Resolved issues

  1. Spoofing risk: Fixed. Middleware uses "strip-and-overwrite" semantics—reserved headers are stripped before claims are extracted and downstream headers are written.
  2. Claim type mismatch: Fixed. Middleware uses StellaOpsClaimTypes.Tenant (stellaops:tenant) with fallback to legacy tid claim.
  3. Scope claim mismatch: Fixed. Middleware extracts scopes from both scp (individual claims) and scope (space-separated) claims.
  4. 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-* and X-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-Tenant
    • X-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):

  1. Strip all reserved identity headers from the incoming request.
  2. Compute effective identity from the authenticated principal:
    • sub from JWT sub (StellaOpsClaimTypes.Subject)
    • tenant from stellaops:tenant (StellaOpsClaimTypes.Tenant)
    • project from stellaops:project (StellaOpsClaimTypes.Project) when present
    • scopes from:
      • scp claims (StellaOpsClaimTypes.ScopeItem) if present, else
      • split scope (StellaOpsClaimTypes.Scope) by spaces
  3. 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)

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: anonymous
    • X-StellaOps-Scopes: (empty) or anonymous (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 identifier
  • Gateway.ProjectId — extracted project identifier (optional)
  • Gateway.Actor — subject/actor from claims or "anonymous"
  • Gateway.ScopesHashSet<string> of scopes
  • Gateway.IsAnonymousbool indicating anonymous request
  • Gateway.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 anonymous identity
  • Legacy headers are written when EnableLegacyHeaders=true
  • Scopes are sorted deterministically
  • 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