Skip to content

fix(auth,gax): align Java mTLS and agent identity certificate handling with Python parity - #14658

Merged
macastelaz merged 4 commits into
googleapis:agentic-identities-bound-tokenfrom
macastelaz:fix-java-mtls-parity
Oct 16, 2026
Merged

macastelaz merged 4 commits into
googleapis:agentic-identities-bound-tokenfrom
macastelaz:fix-java-mtls-parity

Conversation

@macastelaz

Copy link
Copy Markdown
Contributor

Why

An audit comparing the Java (agentic-identities-bound-token) and Python (googleapis/google-cloud-python#18559) agent identity and mTLS implementations surfaced several edge-case behavioral differences:

  1. GAX silent fallback on mTLS initialization error: When CertificateBasedAccess.useMtlsClientCertificate() is true and DefaultMtlsProviderFactory.create() throws an IOException (for example, CertificateSourceUnavailableException when a certificate file disappears mid-read or a configured certificate path is invalid/corrupt), EndpointContext, InstantiatingGrpcChannelProvider, and InstantiatingHttpJsonChannelProvider caught the IOException and silently degraded to mtlsProvider = null (sending requests over an unauthenticated channel or regular endpoint without client certificates).
  2. Startup polling when GOOGLE_API_CERTIFICATE_CONFIG is unset: In AgentIdentityUtils.getWellKnownCertificatePathWithRetry(), when GOOGLE_API_CERTIFICATE_CONFIG was unset and GOOGLE_API_USE_CLIENT_CERTIFICATE=true was set on a non-agent GCE VM where no certificates exist, Java polled the well-known Cloud Run paths for ~3 seconds and threw an IOException on timeout instead of falling back to unbound tokens in 0ms.
  3. Symlinked parent directories and empty (0-byte) files during startup polling:
    • AgentIdentityUtils.isPathInWellKnownDir(String) used Files.exists(path) before calling toRealPath(), which skipped symlink resolution when the leaf certificate file did not yet exist on disk (e.g., on systems where /var/run is a symlink to /run and GOOGLE_API_CERTIFICATE_CONFIG uses /run/secrets/workload-spiffe-credentials/...).
    • AgentIdentityCacheUtils.checkExistsOrAccessDenied(Path) only checked file existence via BasicFileAttributes without verifying attrs.isRegularFile() && attrs.size() > 0, which could cause startup polling to return immediately on a newly created 0-byte file before certificate contents were written.
  4. GOOGLE_API_USE_CLIENT_CERTIFICATE non-boolean values: MtlsUtils.useMtlsClientCertificate() and MtlsUtils.getWorkloadCertPath() only checked for "true" and "false", allowing arbitrary non-boolean values (e.g., "invalid" or "1") to fall through to auto-discovery instead of treating any non-"true" value as disabled.

What changes

  • GAX (CertificateBasedAccess, EndpointContext, InstantiatingGrpcChannelProvider, InstantiatingHttpJsonChannelProvider):
    • Propagate IOException from DefaultMtlsProviderFactory.create() (routed via package-private CertificateBasedAccess.createDefaultMtlsProvider()) when certificateBasedAccess.useMtlsClientCertificate() is true, failing closed instead of degrading to mtlsProvider = null.
    • When useMtlsClientCertificate() is false, IOException during default MtlsProvider creation continues to be logged at FINE and ignored.
  • AgentIdentityUtils & AgentIdentityCacheUtils:
    • Remove the retry loop and IOException throw from getWellKnownCertificatePathWithRetry() when GOOGLE_API_CERTIFICATE_CONFIG is unset; check well-known paths once (GKE_CREDENTIAL_BUNDLE_PATH, WELL_KNOWN_CREDENTIAL_BUNDLE_PATH, WELL_KNOWN_CERT_PATH + WELL_KNOWN_KEY_PATH) and immediately return certsPresent=false if none are present.
    • Startup polling remains exclusively gated on GOOGLE_API_CERTIFICATE_CONFIG pointing inside /var/run/secrets/workload-spiffe-credentials/.
    • Add resolveRealPathAllowingMissing(Path) to resolve symlinks on the nearest existing ancestor directory and re-append remaining relative path components in isPathInWellKnownDir(String).
    • Require attrs.isRegularFile() && attrs.size() > 0 in AgentIdentityCacheUtils.checkExistsOrAccessDenied(Path) so polling waits until certificate files are non-empty regular files (> 0 bytes).
  • MtlsUtils:
    • Treat any non-empty trimmed value of GOOGLE_API_USE_CLIENT_CERTIFICATE other than "true" (case-insensitive) as disabled (false / null) in both useMtlsClientCertificate() and getWorkloadCertPath().

Tests

  • EndpointContextTest, InstantiatingGrpcChannelProviderTest, InstantiatingHttpJsonChannelProviderTest:
    • Added tests verifying that IOException from createDefaultMtlsProvider() is propagated when useMtlsClientCertificate() is true and ignored when useMtlsClientCertificate() is false.
  • AgentIdentityUtilsTest:
    • Updated unset GOOGLE_API_CERTIFICATE_CONFIG tests to verify 0-sleep non-polling behavior and immediate null return when no well-known certificates exist, even when GOOGLE_API_USE_CLIENT_CERTIFICATE=true.
    • Added tests for 0-byte -> populated file polling in getConfigFilePathWithRetry and getWorkloadCertPathFromConfigWithRetry.
    • Added test for isPathInWellKnownDir with symlinked parent directory and non-existent leaf file.
  • MtlsUtilsTest:
    • Added tests verifying non-boolean values of GOOGLE_API_USE_CLIENT_CERTIFICATE ("invalid") return false in useMtlsClientCertificate() and null in getWorkloadCertPath() even when workload certificates exist on disk.

…g with Python parity

- Fail closed in GAX (EndpointContext, InstantiatingGrpcChannelProvider, InstantiatingHttpJsonChannelProvider) when DefaultMtlsProviderFactory.create() throws IOException while useMtlsClientCertificate() is true.
- Only poll on startup for agent identity certificates when GOOGLE_API_CERTIFICATE_CONFIG explicitly points inside the well-known workload SPIFFE directory, resolving parent directory symlinks and requiring non-empty regular files (> 0 bytes).
- Return certsPresent=false immediately without polling or throwing when GOOGLE_API_CERTIFICATE_CONFIG is unset and no well-known certificates exist on disk.
- Treat any non-'true' value of GOOGLE_API_USE_CLIENT_CERTIFICATE as disabled in MtlsUtils.
@macastelaz
macastelaz requested review from a team as code owners October 15, 2026 01:55

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request refactors mTLS configuration and certificate resolution. Key changes include optimizing the handling of the GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable, ensuring that file checks verify that paths are non-empty regular files, resolving symlinks while preserving trailing missing elements, and removing startup polling when the certificate configuration is unset. Additionally, the default MtlsProvider creation is refactored to propagate IOExceptions rather than silently falling back to non-mTLS channels. Review feedback suggests optimizing useMtlsClientCertificate by avoiding redundant environment variable fetches and adding logging for unexpected exceptions caught during certificate checks in AgentIdentityUtils.

@macastelaz
macastelaz requested a review from nbayati October 15, 2026 02:53

@nbayati nbayati left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Just a few nits


AgentIdentityUtils.CertInfo info = AgentIdentityUtils.getAgentIdentityCertInfo();
assertNotNull(info);
assertEquals(2, fakeTime.getSleepCount());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This test still passes if the attrs.size() > 0 check in checkExistsOrAccessDenied is reverted. Without the check, the 0-byte files count as ready and polling returns right away, but loadAndVerifyCredentials then makes 3 read attempts with a timeService.sleep between them on the same fake clock. The callback fills in the cert and key on those two retry sleeps, so the test still ends with 2 sleeps and a valid CertInfo.

Does it make sense to fill the files in after the load retries would have run out (e.g. the cert on sleep 4 and the key on sleep 5, and assert 5 sleeps)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch. Updated the test in a14152d5ca4 to populate the certificate on sleep 4 and the private key on sleep 5, and assert 5 total sleeps so loadAndVerifyCredentials's 3 attempts (2 sleeps) cannot mask a missing 0-byte check.

}

@Test
void endpointContextBuild_whenDefaultMtlsProviderThrowsIOException_propagatesIOException()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: Could we also add a test where createDefaultMtlsProvider() throws CertificateSourceUnavailableException (here and in the two channel provider tests)? That covers the GOOGLE_API_USE_CLIENT_CERTIFICATE=true fallback when no certs exist on disk, where build() should still succeed without mTLS on the regular endpoint.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in a14152d5ca4. Added unit tests covering CertificateSourceUnavailableException from createDefaultMtlsProvider() in EndpointContextTest, InstantiatingGrpcChannelProviderTest, and InstantiatingHttpJsonChannelProviderTest.


/**
* Resolves symlinks along all existing prefix components of {@code path}, preserving any trailing
* non-existent path elements (matching Python's {@code os.path.realpath} behavior before startup

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: this doesn't follow a symlink whose target doesn't exist yet (e.g. a config path outside the well-known dir that links to a file Cloud Run hasn't written), so it isn't quite os.path.realpath. That's fine for the /var/run -> /run case this targets, but could we reword the comment so it doesn't claim full parity?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in a14152d5ca4. Reworded the Javadoc on resolveRealPathAllowingMissing to note that it resolves symlinks on existing ancestor directories (such as /var/run to /run before startup files are created) without claiming full os.path.realpath parity.

@macastelaz
macastelaz merged commit c40c533 into googleapis:agentic-identities-bound-token Oct 16, 2026
263 checks passed
macastelaz added a commit that referenced this pull request Oct 19, 2026
…r agent identities (#14668)

## Summary

Merges the `agentic-identities-bound-token` feature branch into `main`,
adding end-to-end support for certificate-bound tokens and automatic
mTLS with transparent certificate rotation for agent identities on Cloud
Run and GKE.

### `google-auth-library-java` (`oauth2_http`)

- **Default certificate-bound tokens (`#13873`, `#14595`, `#14658`):**
When an agent identity SPIFFE workload certificate is present (either
via `GOOGLE_API_CERTIFICATE_CONFIG` on Cloud Run or the GKE credential
bundle at
`/var/run/secrets/workload-spiffe-credentials/x509.credential-bundle.private-key.pem`),
`ComputeEngineCredentials` requests certificate-bound access tokens and
ID tokens via `POST` to the Metadata Server with the leaf certificate
chain.
- **Opt-out controls (`#13873`, `#14559`, `#14557`):**
- Process-wide: `GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN=false` (or legacy
`GOOGLE_API_PREVENT_AGENT_TOKEN_SHARING_FOR_GCP_SERVICES=false`)
disables token binding.
- Per-target ID tokens: `IdTokenProvider.Option.DISABLE_BOUND_ID_TOKEN`
requests an unbound ID token for specific targets (such as plain HTTPS
`*.run.app` URLs) while keeping other ID tokens and access tokens bound.
- **Automatic mTLS & strict configuration (`#13995`, `#14595`,
`#14658`):**
- `MtlsUtils.useMtlsClientCertificate()` enables mTLS automatically when
a workload certificate config or GKE credential bundle is present,
unless `GOOGLE_API_USE_CLIENT_CERTIFICATE` is set to a non-`"true"`
value.
- Missing, unreadable, or malformed explicit
`GOOGLE_API_CERTIFICATE_CONFIG` files fail closed with
`IllegalStateException`.
- **Full mTLS certificate chain (`#14621`):** `X509Provider` and
`SecureConnectProvider` now populate the mTLS key store with the full
certificate chain (leaf + intermediates) via
`MtlsKeyStoreUtils.createMtlsKeyStore` rather than only the leaf
certificate.

### `sdk-platform-java/gax-java` (`gax`, `gax-grpc`, `gax-httpjson`)

- **Transparent retries on mTLS certificate rotation (`#13995`):**
- `CertificateRotationTracker` checks the SHA-256 fingerprint of the
workload certificate on disk after an `UNAUTHENTICATED` failure.
- `ChannelPool` (gRPC) and `RefreshingHttpJsonChannel` (HTTP/JSON)
rebuild transport channels with the rotated certificate without
interrupting in-flight RPCs.
- `AttemptCallable` (unary) and `ServerStreamingAttemptCallable` (server
streaming) issue one immediate free retry on the refreshed channel when
a certificate rotation is detected.
- **Fail-closed default mTLS provider loading (`#14658`):** When
`useMtlsClientCertificate()` is `true`, `EndpointContext`,
`InstantiatingGrpcChannelProvider`, and
`InstantiatingHttpJsonChannelProvider` propagate `IOException`s (while
still falling back cleanly on `CertificateSourceUnavailableException`
when no certificate source exists on disk).
- **Conscrypt TLS 1.3 mTLS fix (`#14556`):**
`InstantiatingHttpJsonChannelProvider` uses Conscrypt's own
`TrustManagerFactory` and `KeyManagerFactory` for the mTLS `SSLContext`,
fixing `Unknown authType: GENERIC` on TLS 1.3 and key-manager algorithm
constraint mismatches on JDK 26+.

## Sub-PRs Included on `agentic-identities-bound-token`

| PR | Description |
| :--- | :--- |
| `#13873` | `feat: Enable Bound Token for Agentic Identities` |
| `#13995` | `feat(gax): support transparent retries during mTLS
certificate rotations` |
| `#14556` | `fix(gax-httpjson): use Conscrypt TrustManagerFactory for
mTLS SSLContext` |
| `#14559` | `feat(auth): add DISABLE_BOUND_ID_TOKEN option to opt out
of certificate-bound ID tokens` |
| `#14557` | `docs(auth): document certificate-bound tokens for agent
identities` |
| `#14595` | `feat(auth): support GKE agent identity credential bundle
for bound tokens and mTLS` |
| `#14626` | `fix(gax): drop jsr305 annotations left over after syncing
with main` |
| `#14621` | `fix(auth): present the full certificate chain for mTLS` |
| `#14658` | `fix(auth,gax): align Java mTLS and agent identity
certificate handling with Python parity` |

## Testing

- **Automated tests:** Full unit test suites in
`google-auth-library-java` (`credentials`, `oauth2_http`) and
`sdk-platform-java/gax-java` (`gax`, `gax-grpc`, `gax-httpjson`) pass
across JDK 8, 11, 17, 21, 25, and 26, including new unit tests for
certificate discovery, bound token requests, startup polling, rotation
retries, Conscrypt TLS 1.3 mTLS handshakes, and full certificate chains.
- **Integrated manual testing (`45/45` scenarios passed):** Built all 9
changed artifacts (`google-auth-library-credentials`,
`google-auth-library-oauth2-http`, `api-common`, `gax`, `gax-grpc`,
`gax-httpjson`, `google-cloud-core`, `google-cloud-core-http`,
`google-cloud-core-grpc`) together from the combined branch and ran 22
live GAPIC mTLS rotation scenarios, 14 local bound-token and GAX parity
checks, 3 live Cloud Run Agent Identity job executions, and 6 live GKE
Agent Identity pod scenarios.

<details>
<summary>Full manual test matrix (45 scenarios)</summary>

### 1. Live GAPIC mTLS Rotation (`22/22` Scenarios, `125/125` Checks)

Executed real `KeyManagementServiceClient` (gRPC and HTTP/JSON) and
`BigQueryReadClient` (server-streaming `ReadRows`) against live
`.mtls.googleapis.com` endpoints (`cloudkms.mtls.googleapis.com`,
`bigquerystorage.mtls.googleapis.com`) while rotating client
certificates on disk.

| Group | Scenarios | What Was Verified | Result |
| :--- | :--- | :--- | :--- |
| **Unary gRPC (`G1`–`G7`)** | `atomic-mv`, `inplace`, `symlink-swap`,
`corrupt-recover`, `missing-recover`, `idle-gap`, `burst-25-rpcs` |
`KeyManagementServiceClient` gRPC channel seamlessly picks up rotated
certs across atomic rename, in-place write, Kubernetes-style symlink
swap, corrupt/missing file recovery, idle gaps, and concurrent bursts. |
**7/7 Passed** |
| **Server-Streaming gRPC (`S1`–`S4`)** | `mid-stream`,
`between-streams`, `reconnect-after-rotate`, `corrupt-mid-stream` |
`BigQueryReadClient.readRowsCallable()` active streams continue
uninterrupted during rotation; subsequent streams and reconnects use the
new certificate. | **4/4 Passed** |
| **Unary HTTP/JSON (`H1`–`H5`)** | `atomic-mv`, `inplace`,
`corrupt-recover`, `missing-recover`, `burst-20-rpcs` |
`KeyManagementServiceClient` HTTP/JSON transport reloads rotated certs
and recovers from transient corrupt/missing files without dropping
requests. | **5/5 Passed** |
| **Startup & Providers (`N1a`, `N1b`, `C1`)** | `N1a`, `N1b`,
`C1-conscrypt`, `C1-jdk` | Missing cert config handling at startup; full
rotation works under both Conscrypt and JDK (`SunJSSE`) TLS providers. |
**4/4 Passed** |
| **Combined Assertions** | `G1`–`S4` assertions | Verified `125/125`
individual assertions across 22 isolated JVMs. | **22/22 Passed** |

### 2. Local Bound-Token & GAX Parity (`14/14` Checks)

| Check | PR / Area | What Was Verified | Result |
| :--- | :--- | :--- | :--- |
| **Scenarios 1–7** | `#13873` / `#14595` | Default bound token
(`cnf.x5t#S256` matches leaf),
`GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN=false`,
`GOOGLE_API_USE_CLIENT_CERTIFICATE=false`, cert rotation updates
`x5t#S256`, and
`GOOGLE_API_PREVENT_AGENT_TOKEN_SHARING_FOR_GCP_SERVICES` behavior. |
**Passed** |
| **Scenario 8d** | `#14658` | `GOOGLE_API_USE_CLIENT_CERTIFICATE=true`
with no certs falls back immediately (`43ms` GET) with no 30s polling
delay. | **Passed** |
| **Scenario 8e** | `#14658` | Invalid explicit
`GOOGLE_API_CERTIFICATE_CONFIG` fails closed with `IOException` instead
of silently downgrading. | **Passed** |
| **Scenario 10** | `#14658` | 0-byte startup cert file is polled until
populated rather than failing immediately. | **Passed** |
| **Scenario 11** | `#14658` | Kubernetes `..data` symlinked parent
directory resolves to the real directory for file watching. | **Passed**
|
| **Scenario 12a** | `#14559` |
`IdTokenProvider.Option.DISABLE_BOUND_ID_TOKEN` returns an unbound ID
token without leaking state to subsequent default calls. | **Passed** |
| **Scenario 12b** | `#14621` | `X509Provider` preserves the full
2-certificate chain (`leaf + intermediate`) in the `KeyStore`. |
**Passed** |
| **GAX `fail-closed`** | `#14658` | Missing explicit
`GOOGLE_API_CERTIFICATE_CONFIG` throws `IllegalStateException` in both
`InstantiatingGrpcChannelProvider` and
`InstantiatingHttpJsonChannelProvider`. | **Passed** |
| **GAX `zero-ms-fallback`** | `#14658` |
`GOOGLE_API_USE_CLIENT_CERTIFICATE=true` with no certs builds plain gRPC
and HTTP/JSON channels immediately (`0ms` polling delay). | **Passed** |

### 3. Live Cloud Run Agent Identity (`3/3` Scenarios)

Tested on a Cloud Run Agent Identity job (`--functional-type=agent
--identity-type=agent-identity`).

| Scenario | Env Vars | Key Results |
| :--- | :--- | :--- |
| **Default (Bound)** | *(unset)* | `DefaultMtlsProviderFactory` chain
length = `2` (`#14621`). Access token acquired in `414ms`. ID token
`cnf.x5t#S256` matched leaf cert. `DISABLE_BOUND_ID_TOKEN` (`#14559`)
returned `cnf: false` and next default call restored `cnf: true`. GAX
`ProjectsClient` (gRPC & HTTP/JSON) resolved
`cloudresourcemanager.mtls.googleapis.com:443` and passed mTLS +
bound-token auth (`PERMISSION_DENIED` IAM check). Non-mTLS transport
rejected bound token (`401`), true mTLS passed auth (`403`), and
mismatched client cert was rejected (`401`). |
| **Runtime Bound Opt-Out** |
`GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN=false` | Unbound access token and
ID token (`cnf` absent). Non-mTLS endpoints passed auth (`403` IAM) and
live Java ADK `LlmAgent` + `Gemini` call on Vertex AI succeeded (`200
OK`). |
| **Client Cert Opt-Out** | `GOOGLE_API_USE_CLIENT_CERTIFICATE=false` |
Unbound access token (`450ms`) and ID token (`cnf` absent). GAX
`ProjectsClient` (gRPC & HTTP/JSON) resolved plain
`cloudresourcemanager.googleapis.com:443` and passed auth
(`PERMISSION_DENIED`). Live Java ADK `LlmAgent` + `Gemini` call on
Vertex AI succeeded (`200 OK`). |

### 4. Live GKE Agent Identity (`6/6` Scenarios)

Tested on a GKE Agent Identity cluster against live GCP APIs
(`cloudresourcemanager`, `storage`) and an in-cluster SPIFFE mTLS
receiver configured to trust **only** the SPIFFE root CA (requiring the
full leaf + intermediate chain from `#14621`).

| Scenario | Pod Annotations | Env Vars | Key Results |
| :--- | :--- | :--- | :--- |
| **`s1-default`** | `inject-podcertificates=true` | *(unset)* | `bundle
exists=true`, first token fetch `1430ms`, SDK KeyStore chain length =
`2`, mTLS receiver (SDK KeyStore) = `200 OK`, mTLS GCS passed auth
(`404`), plain GCS rejected bound token (`401`), gRPC & HTTP/JSON
resolved `.mtls.googleapis.com:443` and passed auth,
`DISABLE_BOUND_ID_TOKEN` -> plain `200` / mTLS `401` / default after
opt-out -> mTLS `200` / access token still bound -> plain GCS `401`. |
| **`s2-runtime-bound-false`** | `inject-podcertificates=true` |
`GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN=false` | Unbound tokens
(`cnf=null`), mTLS receiver rejected unbound token (`401`), plain
receiver accepted (`200`), raw GCS and Storage SDK (HTTP & gRPC) passed
auth (`404`), gRPC & HTTP/JSON resolved `.mtls.googleapis.com:443` and
passed auth. |
| **`s3-use-cert-false`** | `inject-podcertificates=true` |
`GOOGLE_API_USE_CLIENT_CERTIFICATE=false` | Unbound tokens (`cnf=null`),
gRPC & HTTP/JSON resolved plain
`cloudresourcemanager.googleapis.com:443` and passed auth, plain
receiver = `200`, plain GCS passed auth (`404`). |
| **`s4-use-cert-true`** | `inject-podcertificates=true` |
`GOOGLE_API_USE_CLIENT_CERTIFICATE=true` | Identical bound behavior to
`s1-default`: SDK KeyStore mTLS receiver = `200`,
`.mtls.googleapis.com:443` resolved and passed auth, all
`DISABLE_BOUND_ID_TOKEN` checks passed. |
| **`s5-no-annotation`** | *(none)* | *(unset)* | `bundle exists=false`,
first token fetch `646ms` (no 30s hang), resolved plain
`cloudresourcemanager.googleapis.com:443`, passed auth
(`PERMISSION_DENIED` / `403`). |
| **`s6-use-cert-true-no-annotation`** | *(none)* |
`GOOGLE_API_USE_CLIENT_CERTIFICATE=true` | Verified `#14658` on real
GKE: `bundle exists=false`, first token fetch `609ms` (no 30s polling
delay), resolved plain `cloudresourcemanager.googleapis.com:443`, passed
auth (`PERMISSION_DENIED` / `403`). |

</details>

BEGIN_COMMIT_OVERRIDE
feat(auth): request certificate-bound tokens for agent identities by
default. Set GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN=false to opt out.
feat(auth): add IdTokenProvider.Option.DISABLE_BOUND_ID_TOKEN to request
unbound ID tokens for specific targets.
feat(auth): support GKE agent identity credential bundle for bound
tokens and mTLS.
fix(auth): present the full certificate chain for mTLS.
docs(auth): known limitation: ADK for Java doesn't support mTLS yet;
agents that use it must set GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN=false.
feat(gax): enable mTLS automatically when a workload certificate is
present and support transparent retries during mTLS certificate
rotations.
fix(gax-httpjson): use Conscrypt TrustManagerFactory and
KeyManagerFactory for mTLS SSLContext.
END_COMMIT_OVERRIDE

---------

Co-authored-by: Kirill Logachev <kirl@google.com>
Co-authored-by: Min Zhu <zhumin@google.com>
Co-authored-by: Lawrence Qiu <lawrenceqiu@google.com>
Co-authored-by: yangyzs <171981480+yangyzs@users.noreply.github.com>
Co-authored-by: cloud-java-bot <122572305+cloud-java-bot@users.noreply.github.com>
Co-authored-by: Keshav Dandeva <keshavdandeva@google.com>
Co-authored-by: Mattie Fu <mattiefu@google.com>
Co-authored-by: Will Howes <whowes@google.com>
Co-authored-by: Jin Seop Kim <jinseop@google.com>
Co-authored-by: Jiang Zhu <jiangzzhu@google.com>
Co-authored-by: Knut Olav Løite <koloite@gmail.com>
Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
Co-authored-by: rahul2393 <irahul@google.com>
Co-authored-by: Sakthivel Subramanian <179120858+sakthivelmanii@users.noreply.github.com>
Co-authored-by: Gaole Meng <gaole@google.com>
Co-authored-by: Pranav Iyer <pjiyer@google.com>
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.

2 participants