Skip to content

google-auth-library: X.509 workload identity federation (mTLS) ignores HTTPS_PROXY for the STS token exchange #9594

Open

Description

@moibueno

Library Name

google-auth-library

A screenshot that you have tested with "Try this API".

Not applicable: this is about how the client library routes the STS token request (it ignores the configured HTTPS proxy), not about an API response.

Link to the code that reproduces this issue.

No public repository: the minimal reproduction is inline below, and the code locations are linked in the description.

A step-by-step description of how to reproduce the issue

  1. Run any HTTP proxy that logs CONNECT requests (for example Squid) on 127.0.0.1:3128.
  2. Put a client certificate and key on disk and point a certificate config at them.
  3. Run the script below with HTTPS_PROXY=http://127.0.0.1:3128 (and, on Node 24, NODE_USE_ENV_PROXY=1 as well).
  4. The proxy logs no CONNECT for sts.mtls.googleapis.com. The token request opens a direct TLS connection. In an environment where outbound traffic must go through the proxy, the request fails and the client can't get a token.
import {writeFileSync} from 'node:fs';
import {ExternalAccountClient} from 'google-auth-library';

writeFileSync('/tmp/certificate_config.json', JSON.stringify({
  cert_configs: {workload: {cert_path: '/tmp/client.pem', key_path: '/tmp/client.key'}},
}));

const client = ExternalAccountClient.fromJSON({
  type: 'external_account',
  audience: '//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider',
  subject_token_type: 'urn:ietf:params:oauth:token-type:mtls',
  token_url: 'https://sts.mtls.googleapis.com/v1/token',
  credential_source: {certificate: {certificate_config_location: '/tmp/certificate_config.json'}},
});

// The STS exchange connects straight to sts.mtls.googleapis.com:443.
await client.getAccessToken();

A clear and concise description of what the bug is, and what you expected to happen.

With X.509 workload identity federation (credential_source.certificate), the token exchange with sts.mtls.googleapis.com and the service account impersonation call don't use HTTPS_PROXY.

CertificateSubjectTokenSupplier.createMtlsHttpsAgent() returns new https.Agent({key, cert}), and IdentityPoolClient.retrieveSubjectToken() passes that agent to new Gaxios({agent: mtlsAgent}) for the STS credential and for the client transporter. When an agent is given, gaxios skips its proxy handling ("use the user-provided agent"), so HTTPS_PROXY has no effect. Node's built-in proxy support doesn't help either: NODE_USE_ENV_PROXY only configures the global agent, and a custom agent needs its own proxyEnv option.

I checked this in 10.9.1 and in the current source of core/packages/google-auth-library-nodejs (11.2.0), where createMtlsHttpsAgent() is unchanged.

Expected: the mTLS requests go through the proxy configured in the environment (CONNECT tunnel, with TLS and the client certificate end to end), the same as the other requests the library makes.

A clear and concise description WHY you expect this behavior

Requests that reach gaxios without a prebuilt agent honor HTTPS_PROXY: gaxios builds an HttpsProxyAgent for them, and it even forwards cert and key to that proxy agent. The X.509 federation path builds its own agent instead and loses the proxy. Deployments that restrict egress to a forward proxy can't use certificate-based workload identity federation without patching the library.

Two possible fixes: pass cert and key to Gaxios instead of a prebuilt agent, so that the existing proxy branch applies; or, on Node 24.5 and later, create the agent with proxyEnv: process.env.

Our workaround replaces createMtlsHttpsAgent on the client's subject token supplier so that it returns new https.Agent({key, cert, proxyEnv: process.env}). With it, a test with a local CONNECT proxy and a token endpoint that requires a client certificate shows the exchange going through the tunnel, with the certificate presented to the endpoint. Without it, the proxy sees no CONNECT. This depends on a private field, which is why we'd prefer a fix in the library.

Environment: Node.js 24.18 on Debian 12, google-auth-library 10.9.1, gaxios 7.3.1.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions