P3.4: Inject X-Forwarded-User identity headers after JWT validation #8

Closed
opened 2026-09-08 03:55:45 +00:00 by rock · 0 comments
Owner

Summary

After JWT validation, gateway injects identity headers into proxied requests so downstream services get audit trail without parsing JWTs themselves.

Headers

X-Forwarded-User: rock              # from sub claim
X-Forwarded-Roles: llm:inference    # from roles or permissions claim
X-Acting-Service: portfolio-agent   # from azp claim (only for service accounts)
X-Auth-Verified: true               # gateway validated the JWT

Why

  • Downstream services (memory, SQS) need to know WHO is calling for audit and authorization
  • Downstream should NOT need JWKS/JWT validation logic — gateway already did it
  • Network policy guarantees only gateway can reach downstream, so headers are trustworthy
  • Enables per-user audit logging at downstream without coupling to Authentik

Acceptance Criteria

  • Headers injected after successful JWT validation
  • X-Forwarded-User set from sub claim
  • X-Forwarded-Roles set from roles or permissions claim (comma-separated)
  • X-Acting-Service set from azp claim (omitted if same as sub)
  • X-Auth-Verified: true always set when JWT passed validation
  • Incoming X-Forwarded-User / X-Auth-Verified stripped from client request (anti-spoofing)
  • Unauthenticated requests (no JWT) do NOT get these headers
  • Unit tests: header injection, header stripping, claim extraction edge cases

Implementation

  • In proxy.go ServeHTTP, after JWT validation block
  • Strip incoming spoofed headers before routing

Security contract

Downstream MUST verify X-Auth-Verified: true before trusting other X-Forwarded- headers.* If header is absent, request was not authenticated at gateway (e.g. health checks, pre-auth endpoints).

Review checklist

  • Incoming X-Forwarded-User stripped before injection (no client spoofing)
  • X-Auth-Verified only set after real JWT validation (never unconditionally)
  • Wildcard permissions ("") not expanded in header — passed as literal ""
  • Empty claims handled gracefully (no empty header values)
## Summary After JWT validation, gateway injects identity headers into proxied requests so downstream services get audit trail without parsing JWTs themselves. ## Headers ``` X-Forwarded-User: rock # from sub claim X-Forwarded-Roles: llm:inference # from roles or permissions claim X-Acting-Service: portfolio-agent # from azp claim (only for service accounts) X-Auth-Verified: true # gateway validated the JWT ``` ## Why - Downstream services (memory, SQS) need to know WHO is calling for audit and authorization - Downstream should NOT need JWKS/JWT validation logic — gateway already did it - Network policy guarantees only gateway can reach downstream, so headers are trustworthy - Enables per-user audit logging at downstream without coupling to Authentik ## Acceptance Criteria - [ ] Headers injected after successful JWT validation - [ ] `X-Forwarded-User` set from `sub` claim - [ ] `X-Forwarded-Roles` set from `roles` or `permissions` claim (comma-separated) - [ ] `X-Acting-Service` set from `azp` claim (omitted if same as sub) - [ ] `X-Auth-Verified: true` always set when JWT passed validation - [ ] Incoming `X-Forwarded-User` / `X-Auth-Verified` stripped from client request (anti-spoofing) - [ ] Unauthenticated requests (no JWT) do NOT get these headers - [ ] Unit tests: header injection, header stripping, claim extraction edge cases ## Implementation - In `proxy.go` ServeHTTP, after JWT validation block - Strip incoming spoofed headers before routing ## Security contract **Downstream MUST verify `X-Auth-Verified: true` before trusting other X-Forwarded-* headers.** If header is absent, request was not authenticated at gateway (e.g. health checks, pre-auth endpoints). ## Review checklist - [ ] Incoming X-Forwarded-User stripped before injection (no client spoofing) - [ ] X-Auth-Verified only set after real JWT validation (never unconditionally) - [ ] Wildcard permissions ("*") not expanded in header — passed as literal "*" - [ ] Empty claims handled gracefully (no empty header values)
rock added this to the Phase 3: OAuth2 Token Exchange & Identity Propagation milestone 2026-09-08 03:55:45 +00:00
rock added the area/autharea/gatewaytype/featurestatus/todopriority/high labels 2026-09-08 03:55:46 +00:00
rock self-assigned this 2026-09-08 03:55:47 +00:00
rock added this to the (deleted) project 2026-09-08 04:37:13 +00:00
rock closed this issue 2026-09-08 04:45:06 +00:00
Sign in to join this conversation.