Expand description
OAuth2 client-credentials proxying (DW-035, feature analysis 4.6).
The gateway acts as an OAuth2 client itself: for an upstream with an
crate::config::OAuth2ClientCredentials block, it obtains an
access token from an external token endpoint using the
client-credentials grant (RFC 6749 section 4.4) and forwards that
token to the upstream as Authorization: Bearer <token> (replacing
any client-supplied Authorization header — the upstream sees the
gateway’s token, not the client’s). This is for service-to-service
calls where the gateway authenticates to the upstream with a token
it obtains from an OAuth2 token endpoint.
§Flow
- Before forwarding a request to an upstream with
oauth2_client_credentialsconfigured, the gateway checks its token cache (per upstream, keyed by upstream name on the dataplane) for a valid token. - If no valid token, it POSTs to the token endpoint with
grant_type=client_credentials, theclient_idandclient_secretas HTTP Basic auth (RFC 6749 section 2.3.1), andscope(space-joined) when configured. - The response (
{access_token, token_type, expires_in, scope}) is cached with a TTL ofmin(expires_in - skew, token_cache_ttl_s)(or justexpires_in - skewwhen no override is set). The skew (60 s) avoids using a token that expires while an in-flight request is still streaming. - The gateway adds
Authorization: Bearer <token>to the upstream request, REPLACING any existingAuthorizationheader from the client.
§Caching
The cache is a Mutex<HashMap<UpstreamName, CachedToken>> on the
dataplane. Token refresh is LAZY (on the first request after
expiry) — no background refresh task. A token is considered valid
while Instant::now() < expires_at; an expired entry is overwritten
by the next fetch. Concurrent fetches for the same upstream
serialize on a per-upstream tokio::Mutex so a token-endpoint
fetch-storm cannot be driven by concurrent requests (the JWKS
refresh-lock precedent).
§mTLS to the token endpoint
An optional mtls block configures a client certificate for the TLS
handshake to the token endpoint itself (RFC 8705 tls_client_auth).
The token endpoint’s SERVER certificate is verified against the
webpki public roots by default; a private-CA token endpoint is not
supported in this edition (the trusted_ca_file for the token
endpoint would be a separate config field; deferred until an
operator asks for it).
§Error posture
A token-endpoint failure (network error, non-2xx response, malformed
body) surfaces to the client as 502 oauth2_token_unavailable — the
gateway cannot authenticate to the upstream and refuses to forward
without a token (never proxying unauthenticated). The error envelope
never leaks the token endpoint’s response body or headers.
Structs§
- OAuth2
Client - One upstream’s OAuth2 client-credentials configuration at runtime:
the resolved config plus a per-upstream HTTP client and a fetch
serialization lock. The token cache lives on the dataplane
(
OAuth2TokenCache) so a config reload reuses cached tokens. - OAuth2
Token Cache - Per-upstream OAuth2 token cache (DW-035): lives on the dataplane so a config reload reuses cached tokens (the JWKS cache precedent). Entries are keyed by upstream name; each holds the cached token and its skew-adjusted expiry. Refresh is lazy (on the first request after expiry) — no background task.
Enums§
- OAuth2
Error - Error acquiring a token (DW-035). All variants surface to the client
as 502
oauth2_token_unavailable; theDisplaytext is logged, not sent to the client (the envelope never leaks upstream internals).