Skip to content

docs(auth): document certificate-bound tokens for agent identities - #14557

Merged
macastelaz merged 4 commits into
googleapis:agentic-identities-bound-tokenfrom
macastelaz:docs-agent-bound-tokens
Oct 10, 2026
Merged

macastelaz merged 4 commits into
googleapis:agentic-identities-bound-tokenfrom
macastelaz:docs-agent-bound-tokens

Conversation

@macastelaz

@macastelaz macastelaz commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Important

Merge after #14559, which adds IdTokenProvider.Option.DISABLE_BOUND_ID_TOKEN referenced here. Both must merge before the agentic-identities-bound-token feature branch is merged to main.

What this adds

Docs only; no code changes.

  • ComputeEngineCredentials class Javadoc: adds a brief summary noting that when a workload certificate for an agent identity is available, ComputeEngineCredentials requests certificate-bound access tokens and ID tokens by default, with pointers to IdTokenProvider.Option.DISABLE_BOUND_ID_TOKEN and the Google Auth Library guide.
  • The full "Certificate-bound tokens for agent identities" user guide section (covering environment variables, per-target ID token opt-out, and the ADK for Java limitation) is being added to the DevSite getting-started-with-google-auth-library page (cl/996749889) alongside the Cloud Run documentation (cl/990396526).

Release notes

google-auth-library-java/CHANGELOG.md is generated, so release notes come from commit messages. Suggested text for the BEGIN_COMMIT_OVERRIDE block of the feature-branch → main merge PR:

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

Testing

  • google-java-format and javadoc:javadoc pass.

@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 introduces support for certificate-bound access and ID tokens for agent identities within ComputeEngineCredentials. It adds a new AgentIdentityUtils utility class to manage certificate discovery, SPIFFE trust domain validation, cryptographic key-pair verification, and credential caching. ComputeEngineCredentials is updated to request bound tokens via POST requests to the metadata server when a workload certificate is available. Feedback on the changes suggests improving code readability in the test suite by avoiding fully qualified class names when the class is already imported.

Comment on lines +1407 to +1408
Files.write(keyPath, mismatchedKeyPem.getBytes(StandardCharsets.UTF_8));
Files.setLastModifiedTime(

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.

medium

Avoid using fully qualified class names (such as java.nio.file.attribute.FileTime) if there is no class name conflict in the file and the class is already imported, as it unnecessarily reduces code readability.

    Files.setLastModifiedTime(
        keyPath, FileTime.fromMillis(System.currentTimeMillis() + 10000));
References
  1. Do not use fully qualified class names if there is no class name conflict in the file and the class is already imported, as it unnecessarily reduces code readability.

@macastelaz
macastelaz force-pushed the docs-agent-bound-tokens branch from 145cc34 to 70f3109 Compare October 6, 2026 15:04
@macastelaz macastelaz changed the title docs(auth): document certificate-bound tokens for agent identities [blocked on #13873] docs(auth): document certificate-bound tokens for agent identities Oct 6, 2026
macastelaz added a commit that referenced this pull request Oct 6, 2026
…igquery pom (#14587)

## Why

Every PR into `agentic-identities-bound-token` is currently failing CI
(for example #14559 and #14557, about 35 jobs each) with:

```
[ERROR] 'dependencies.dependency.(groupId:artifactId:type:classifier)' must be unique: com.google.cloud:google-cloud-storage:jar -> duplicate declaration of version (?)
[ERROR] 'dependencies.dependency.(groupId:artifactId:type:classifier)' must be unique: com.google.cloud:google-cloud-datacatalog:jar -> duplicate declaration of version 1.102.0-SNAPSHOT
[ERROR] The build could not read 1 project
```

`java-bigquery/google-cloud-bigquery/pom.xml` declares
`google-cloud-storage` and `google-cloud-datacatalog` twice in
`<dependencies>`. This has been the case for a while, but Maven 3.9 only
logged a `[WARNING]`. The GitHub `ubuntu-24.04` runner image `20261004`
upgraded Maven from 3.9.16 to 3.10.0, which treats it as an error, so
any job that loads the reactor now fails immediately.

## Change

Remove the second declaration of each dependency (11 lines, one file):

- `google-cloud-storage`: drops the later test-scoped entry. This is the
same change as #14586 on `main`.
- `google-cloud-datacatalog`: drops the second, identical test-scoped
entry. `main` no longer has this dependency in this pom, so #14586
doesn't need it.

## Verification

- `mvn help:effective-pom` on the module before and after: the effective
`<dependencies>` section is identical. `google-cloud-storage` stays
test-scoped because the parent's `dependencyManagement` sets
`<scope>test</scope>`, and the two datacatalog entries were identical.
- Each artifact now appears once in `<dependencies>`; the pom parses as
valid XML.
Adds a README section and ComputeEngineCredentials class Javadoc covering:
- bound tokens are the default when an agent identity workload certificate
  is present, and require mTLS with the same certificate
- when a bound token is requested (cert discovery, agent SPIFFE trust domain)
- opt-out env vars and precedence: GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN over
  the legacy GOOGLE_API_PREVENT_AGENT_TOKEN_SHARING_FOR_GCP_SERVICES, and
  GOOGLE_API_USE_CLIENT_CERTIFICATE=false also disabling binding
- the process-wide scope of the opt-out
- requesting unbound ID tokens for specific targets with
  IdTokenProvider.Option.BIND_ID_TOKEN_FALSE
- the ADK for Java known limitation and its workaround
@macastelaz
macastelaz force-pushed the docs-agent-bound-tokens branch from 70f3109 to d02e19a Compare October 6, 2026 17:08
@macastelaz
macastelaz marked this pull request as ready for review October 6, 2026 17:27
@macastelaz
macastelaz requested review from a team as code owners October 6, 2026 17:27
@macastelaz
macastelaz requested review from lqiu96 and nbayati October 6, 2026 17:27
Comment thread google-auth-library-java/README.md Outdated
See the [API Documentation](https://cloud.google.com/java/docs/reference/google-auth-library/latest/overview.html) to see
the Javadocs for Google Auth Library.

## Certificate-bound tokens for agent identities

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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.

Thanks for the pointer! Removed the section from README.md in 2876c2d and moved it to the DevSite getting-started-with-google-auth-library guide (cl/996749889).

Comment on lines +88 to +94
* <p><b>Certificate-bound tokens for agent identities.</b> When a workload certificate for an agent
* identity is available (for example, on Cloud Run with an agent identity), {@link
* #refreshAccessToken()} and {@link #idTokenWithAudience(String, List)} request tokens that are
* bound to that certificate. The certificate is located through the file named by the {@code
* GOOGLE_API_CERTIFICATE_CONFIG} environment variable or, if that is unset, in the default workload
* credentials directory. A bound token is only requested when the certificate's SPIFFE ID belongs
* to an agent identity trust domain; otherwise these credentials return unbound tokens as before.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Might be better to put this in the README/ docs.google.com page (feature of ComputeEngineCredentials).

(My opinion) perhaps can opt to keep javadocs of a Credential class general to what a credential type is about and a feature can have a dedicated section elsewhere. WDYT?

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.

Sounds good! Trimmed the ComputeEngineCredentials class Javadoc down to a brief summary with a pointer to IdTokenProvider.Option.DISABLE_BOUND_ID_TOKEN and the official guide in 2876c2d, and moved the detailed section to the DevSite guide (cl/996749889).

@macastelaz
macastelaz requested a review from lqiu96 October 9, 2026 21:30
macastelaz added a commit that referenced this pull request Oct 10, 2026
…te-bound ID tokens (#14559)

> [!IMPORTANT]
> **This must merge before the `agentic-identities-bound-token` feature
branch is merged to `main`.** Docs PR #14557 documents this option.

## Why

When an agent identity certificate is available,
`ComputeEngineCredentials` requests certificate-bound ID tokens by
default. A bound ID token is only accepted by targets that authenticate
the caller over mTLS with the same certificate. Targets reached over
standard HTTPS, such as a Cloud Run service's `*.run.app` URL or a
custom domain, reject it with `401 Unauthorized`.

Per the [ID token binding
1-pager](https://docs.google.com/document/d/1yL_xdDimlUlUyid6HDtqZUIMQjX9VznwzXn1SBABjZc/edit?pli=1&resourcekey=0-GxSXGdfH5Ni_nSI9f7xkQg&tab=t.0#heading=h.94fnt3dk2gx)
(Sep 30 decision), ID tokens stay bound by default, and callers get a
per-target `bind_id_token` setting to turn binding off where needed. The
process-wide opt-out (`GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN=false`) is
unchanged.

## What changes

- New `IdTokenProvider.Option.DISABLE_BOUND_ID_TOKEN`. Callers pass it
per target, the same way as `FORMAT_FULL` and `LICENSES_TRUE`:

  ```java
  IdTokenCredentials.newBuilder()
      .setIdTokenProvider((IdTokenProvider) credentials)
      .setTargetAudience("https://my-service-12345.us-central1.run.app")

.setOptions(Arrays.asList(IdTokenProvider.Option.DISABLE_BOUND_ID_TOKEN))
      .build();
  ```

- `ComputeEngineCredentials.idTokenWithAudience()`: with the option, the
certificate lookup is skipped and the identity endpoint is called with a
plain GET. Without it, behavior is unchanged (bound whenever a
certificate is available and binding isn't turned off by the env var).
- Other credential types (`ServiceAccountCredentials`,
`ImpersonatedCredentials`, `UserCredentials`) already ignore options
they don't use, so the same code works outside agent identity
environments.
- Access tokens are unaffected.

### API shape

`Option` is a set of on/off flags rather than a value, so this adds only
the "off" flag (`DISABLE_BOUND_ID_TOKEN`). Leaving it unset means
"auto", which currently means bound. This matches the 1-pager's
tri-state (unset = auto) without adding an "enable" flag that would do
nothing today; one can be added later without breaking callers if "auto"
starts deciding per target. Python exposes the same control as
`bind_id_token: Optional[bool]` (googleapis/google-cloud-python#18559).

## Tests

### Unit tests

New `ComputeEngineCredentialsTest` cases, all with an agent identity
certificate present and binding enabled:

- `idTokenWithAudience_disableBoundIdToken_requestsUnboundToken`: GET,
no request body, no forced `format=full`.
-
`idTokenWithAudience_disableBoundIdTokenWithFormatFull_requestsUnboundFullToken`:
still GET; `format=full` is kept when the caller asks for it.
- `idTokenWithAudience_disableBoundIdToken_skipsCertificateLookup`: with
a certificate config pointing to a missing file, the default request
fails, but the opted-out request succeeds, which shows the certificate
is never read.
-
`idTokenCredentials_withDisableBoundIdTokenOption_requestsUnboundToken`:
the option set on `IdTokenCredentials` reaches
`ComputeEngineCredentials`.

The existing bound-ID-token tests are unchanged and still pass. Full
`oauth2_http` suite: 1064 tests, 0 failures. `fmt` is clean.

### Live test on GKE

Run on a GKE cluster with agent identity enabled, using a test-only
local merge of this PR with #14595 (GKE support) and #14556 (already on
the base branch). A single process fetches ID tokens through
`IdTokenCredentials` from `ComputeEngineCredentials` (ADC) and sends
them to an agent-to-agent receiver pod. The receiver accepts only bound
tokens over mTLS (whose `cnf` must match the client certificate), and
only unbound tokens over plain HTTP.

| Check | Defaults | `GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN=false` |
|---|---|---|
| ID token with `DISABLE_BOUND_ID_TOKEN` | **unbound** (no `cnf`);
receiver: plain HTTP 200, mTLS 401 | unbound |
| ID token for another target without the option, same credentials,
fetched afterwards | **still bound** (`cnf` = certificate leaf);
receiver: mTLS 200 | unbound |
| Access token after the ID-token opt-out | **still bound** (401 on a
plain, non-mTLS endpoint) | unbound |

The option unbinds only the ID token it's set on. Other targets and
access tokens are unaffected, and it's harmless when binding is already
off.

**Internal links (Googlers only):**
[results](https://paste.googleplex.com/5926225961418752) ·
[harness](https://paste.googleplex.com/5713819712749568) · cluster
[setup](https://paste.googleplex.com/5635555912712192) and
[manifests](https://paste.googleplex.com/6258309762514944) (shared with
#14595)
@macastelaz
macastelaz merged commit 103734a into googleapis:agentic-identities-bound-token Oct 10, 2026
259 of 260 checks passed
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