Skip to content

[auth]: Add outbound upstream credentials#75

Merged
pseudomuto merged 1 commit into
mainfrom
outbound-credentials
Jul 21, 2026
Merged

[auth]: Add outbound upstream credentials#75
pseudomuto merged 1 commit into
mainfrom
outbound-credentials

Conversation

@pseudomuto

Copy link
Copy Markdown
Collaborator

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
pseudomuto requested a review from Copilot July 20, 2026 20:19
@pseudomuto
pseudomuto requested a review from a team as a code owner July 20, 2026 20:19
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.00000% with 8 lines in your changes missing coverage. Please review.

📢 Thoughts on this report? Let us know!

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 additional grpc.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.

Comment thread internal/auth/authenticator.go
Comment thread internal/auth/provider.go
Comment thread internal/auth/static_provider.go
Comment thread e2e/outbound_credential_override_test.go Outdated
Comment thread e2e/inbound_auth_strip_test.go Outdated
Comment thread internal/auth/statictoken.go
Comment thread internal/auth/jwks.go
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
pseudomuto force-pushed the outbound-credentials branch from 005df0b to d4fe154 Compare July 20, 2026 20:40
@pseudomuto
pseudomuto merged commit defab82 into main Jul 21, 2026
6 checks passed
@pseudomuto
pseudomuto deleted the outbound-credentials branch July 21, 2026 10:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants