[auth]: Add outbound upstream credentials#75
Merged
Conversation
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Adds application-layer outbound credentials for upstream connections (e.g., Temporal Cloud API keys) and ensures caller-supplied credentials are not forwarded upstream when headers overlap, strengthening end-to-end authentication/credential isolation in the proxy.
Changes:
- Introduces
CredentialProvider(with a static API key provider) and wires it into upstream proxy dials via additionalgrpc.DialOptions. - Strips consumed inbound auth headers before forwarding and strips/overrides outbound credential headers to ensure the upstream sees exactly one authoritative value.
- Adds unit + e2e full-stack tests to validate header stripping/override behavior across configurations.
Reviewed changes
Copilot reviewed 25 out of 25 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| internal/server/fx_test.go | Updates test stub authenticator to satisfy the expanded Authenticator interface. |
| internal/proxy/server.go | Adds support for injecting additional outbound gRPC dial options (used for per-RPC credentials + interceptors). |
| internal/proxy/server_test.go | Adds a construction test covering WithDialOptions usage. |
| internal/proxy/fx.go | Wires per-upstream credential providers into the proxy dial configuration. |
| internal/proxy/fx_test.go | Adds an fx construction test for a TLS + upstream-credential configuration. |
| internal/config/upstream.go | Extends upstream config to include optional credentials and validates TLS is required when credentials are configured. |
| internal/config/upstream_test.go | Adds coverage ensuring upstream credentials are rejected without TLS and accepted with TLS. |
| internal/config/auth.go | Adds outbound credential configuration types + validation for credential blocks/API key presence. |
| internal/config/auth_test.go | Adds unit tests for outbound credential config validation. |
| internal/auth/statictoken.go | Exposes the consumed header for inbound static-token auth (used for stripping). |
| internal/auth/static_provider.go | Adds a static outbound credential provider implementing PerRPCCredentials. |
| internal/auth/static_provider_test.go | Adds unit tests for the static outbound credential provider. |
| internal/auth/provider.go | Adds credential-provider selection from config and outbound strip interceptors via dial options. |
| internal/auth/provider_test.go | Adds unit tests for config→provider mapping and dial option composition. |
| internal/auth/provider_internal_test.go | Adds a focused internal test ensuring outgoing header stripping preserves other metadata. |
| internal/auth/jwks.go | Exposes the consumed header for inbound JWKS auth (used for stripping). |
| internal/auth/doc.go | Updates package docs to include outbound credential support. |
| internal/auth/authenticator.go | Extends Authenticator with Header() and strips consumed inbound headers before forwarding. |
| internal/auth/authenticator_test.go | Adds tests asserting consumed inbound headers are stripped (and non-consumed headers are preserved). |
| e2e/upstream_credential_socket_test.go | Adds an end-to-end test proving the configured upstream credential reaches the upstream over TLS. |
| e2e/outbound_credential_override_test.go | Adds an end-to-end test proving outbound credentials override forwarded headers even without inbound auth. |
| e2e/inbound_credential_no_leak_test.go | Adds an end-to-end test proving inbound-only auth headers do not leak upstream. |
| e2e/inbound_auth_strip_test.go | Adds an end-to-end test proving inbound strip + outbound override compose correctly. |
| e2e/harness_test.go | Adds a full-stack fx harness + fake TLS upstream used by the e2e tests. |
| e2e/doc.go | Adds package documentation for the new e2e test suite. |
The proxy secured the connection to each upstream with TLS/mTLS but could not present an application-layer credential, so it could not authenticate itself to upstreams that require one, notably Temporal Cloud namespace endpoints that expect an API key. Add a per-upstream credential the proxy attaches to every forwarded request. A CredentialProvider supplies gRPC per-RPC metadata; the Static provider sends a configured API key as a bearer header (upstreams[].credentials.static.apiKey) and requires transport security, so the key never travels over an insecure connection. An absent credentials block is fine; a present-but-empty one fails closed. Inbound authentication and an outbound credential can share the authorization header, so the proxy makes the outbound credential authoritative: the inbound interceptor strips the header it consumed before forwarding, and the outbound dial strips the credential's header from forwarded metadata before adding its own. The upstream therefore sees exactly one value on that header, and a caller's token is never forwarded upstream. End-to-end tests in ./e2e drive the full stack and assert this across configurations.
pseudomuto
force-pushed
the
outbound-credentials
branch
from
July 20, 2026 20:40
005df0b to
d4fe154
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The proxy secured the connection to each upstream with TLS/mTLS but could not present an application-layer credential, so it could not authenticate itself to upstreams that require one, notably Temporal Cloud namespace endpoints that expect an API key.
Add a per-upstream credential the proxy attaches to every forwarded request. A CredentialProvider supplies gRPC per-RPC metadata; the Static provider sends a configured API key as a bearer header (upstreams[].credentials.static.apiKey) and requires transport security, so the key never travels over an insecure connection. An absent credentials block is fine; a present-but-empty one fails closed.
Inbound authentication and an outbound credential can share the authorization header, so the proxy makes the outbound credential authoritative: the inbound interceptor strips the header it consumed before forwarding, and the outbound dial strips the credential's header from forwarded metadata before adding its own. The upstream therefore sees exactly one value on that header, and a caller's token is never forwarded upstream. End-to-end tests in ./e2e drive the full stack and assert this across configurations.