The Identity Contract
After Envoy validates a token or receives an allow decision from the auth service, products need a consistent way to know who is calling. A common pattern is to remove unsafe client headers and add platform-owned headers such as user ID, service ID, tenant, roles, and request ID.
Header Hygiene Example
This example shows the idea: remove sensitive raw inputs and write verified context. Exact formatter support and metadata names depend on your Envoy version and filter configuration, so treat this as a design pattern and test it in your environment.
request_headers_to_add:
- header:
key: x-platform-user
value: "%DYNAMIC_METADATA(envoy.filters.http.jwt_authn:verified_jwt.sub)%"
append_action: OVERWRITE_IF_EXISTS_OR_ADD
- header:
key: x-platform-tenant
value: "%DYNAMIC_METADATA(envoy.filters.http.jwt_authn:verified_jwt.tenant)%"
append_action: OVERWRITE_IF_EXISTS_OR_ADD
request_headers_to_remove:
- authorization
- cookie
Three Authorization Layers
| Layer | Example decision | Owner |
|---|---|---|
| Route-level | Can this identity reach the data explorer product? | Envoy plus central auth service |
| Product-level | Can this user edit this dashboard? | Product service |
| Data-level | Can this user read this governed dataset? | Data policy service or product domain logic |
Why Product Boundaries Matter
Envoy sees requests and headers. It does not naturally understand every business object. If you put every permission rule in the gateway, the gateway becomes a hidden product database. Use Envoy for common gates and product services for domain-specific checks.
Audit Context
Every allowed and denied request should be traceable. At minimum, capture request ID, route, user or service identity, tenant, issuer, audience, policy name, decision, and decision latency.