镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

feat(auth): add bound token support for access and JWT id tokens for Cloud Run - #17698

Merged
nbayati merged 19 commits into
googleapis:cr-bound-id-tokenfrom
nbayati:bound_token_post
Sep 27, 2026
Merged

nbayati merged 19 commits into
googleapis:cr-bound-id-tokenfrom
nbayati:bound_token_post

Conversation

@nbayati

@nbayati nbayati commented Jul 13, 2026 •

Copy link
Copy Markdown
Contributor

This PR adds the following:

  • Switch the MDS token acquisition from a GET to a POST request when the agentic cert is detected.

  • Add get_agent_identity_certificate_and_bytes() utility to read the raw certificate bytes alongside the parsed cert.

  • Update _metadata.get_service_account_token() (for access tokens) and IDTokenCredentials.refresh() (for ID tokens) to send a POST request with the certificate_chain payload instead of a GET request when bound tokens are supported.

  • Update _metadata.get() helper to support method and body params.

  • Add and update unit tests to verify the new POST request flows.


design: go/sdk-mds-bound-token

id token verification:

  • test script: paste/4514812804595712
  • log results: paste/6316867684794368

Note:

  1. This PR relies on the existing pattern of locating the certificates using the path provided by the config file available at GOOGLE_API_CERTIFICATE_CONFIG. It does not currently fallback on checking the well known location if the env var is not set, which would limit the scope to CR, as GKE and GCE don't set this env var.

  2. It uses the same condition to decide if a bound token should be requested for both access token and id token. We might decide to add a separate env var to opt out.

  3. it still uses the existing GOOGLE_API_PREVENT_AGENT_TOKEN_SHARING_FOR_GCP_SERVICES flag to opt out. We might update it and add a new env var, but will keep the old one for backward compatibility.

@nbayati
nbayati requested review from a team as code owners July 13, 2026 05:15
@nbayati
nbayati requested a review from lsirac July 13, 2026 05:16

@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 updates the Google Auth library to request bound tokens from the Compute Engine metadata server using a POST request with the certificate chain in the body, rather than passing a fingerprint in the URL. To support this, get_agent_identity_certificate_and_bytes was introduced to retrieve both the parsed certificate and its raw bytes, and the metadata get helper was updated to support POST requests and bodies. Feedback on the changes suggests simplifying a redundant tuple check in credentials.py by directly unpacking the returned value from get_agent_identity_certificate_and_bytes.

Comment thread packages/google-auth/google/auth/compute_engine/credentials.py Outdated
Comment thread packages/google-auth/google/auth/compute_engine/_metadata.py
Comment thread packages/google-auth/google/auth/compute_engine/_metadata.py Outdated
Comment thread packages/google-auth/google/auth/_agent_identity_utils.py
Comment thread packages/google-auth/google/auth/compute_engine/credentials.py Outdated
Comment thread packages/google-auth/google/auth/compute_engine/credentials.py Outdated
Comment thread packages/google-auth/google/auth/compute_engine/_metadata.py Outdated
Comment thread packages/google-auth/google/auth/compute_engine/_metadata.py Outdated
Comment thread packages/google-auth/tests/compute_engine/test__metadata.py Outdated
Comment thread packages/google-auth/tests/compute_engine/test_credentials.py
Comment thread packages/google-auth/tests/compute_engine/test__metadata.py Outdated
@lsirac

lsirac commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Both ID token unit tests (test_refresh_with_agent_identity and test_refresh_with_agent_identity_opt_out_or_not_agent) return fake certificates from get_agent_identity_certificate_and_bytes(). Please add a unit test where get_agent_identity_certificate_and_bytes() returns (None, None) so the standard fallback path (running without an agent identity certificate on disk) is fully covered.

@nbayati nbayati added the do not merge Indicates a pull request not ready for merge, due to either quality or timing. label Jul 17, 2026
@nbayati

nbayati commented Jul 17, 2026

Copy link
Copy Markdown
Contributor Author

Can't be merged before CR MDS is ready. Currently targeting a date between July 31 and Aug 7.

Comment thread packages/google-auth/google/auth/_agent_identity_utils.py
return_none_for_not_found_error (Optional[bool]): If True, returns None
for 404 error instead of throwing an exception.
method (str): The HTTP method to use for the request. Defaults to "GET".
body (Optional[bytes]): The HTTP request body payload to send. Defaults to None.

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.

Does it make sense to raise a ValueError (or similar) here to "exit early" if a body is specified byt the method is GET. While I think technically valid to include a body in GET requests (most often I think the body just gets ignored), it may lead a caller to think it is getting a bound token when in reality it isn't?

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.

Yes! that's a great suggestion! Done!

(
cert,
cert_bytes,
) = _agent_identity_utils.get_agent_identity_certificate_and_bytes()

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: It looks like both this and should_request_bound_token check GOOGLE_API_PREVENT_AGENT_TOKEN_SHARING_FOR_GCP_SERVICES and call _mtls_helper._check_use_client_cert_env() - I wonder if we can optimize this in any way?

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.

Yeah you're right, they do both check the env var but I don't think we can eliminate it because the two methods have different callers and come from different paths (compute engine and identity pool) so we need to have the check in both places. We could probably do some refactoring, but I'm leaning toward keeping the code as is since the env var reading is not an expensive operation and this way we can keep the methods self contained.

return None
return None, None

return parse_certificate(cert_bytes), cert_bytes

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.

If cert_path points to a combined bundle (credentialbundle.pem), sending raw cert_file.read() puts the private key into certificate_chain over plain HTTP (and GKE MDS rejects non-CERTIFICATE PEM blocks with 400). Also, cert_bytes.decode("utf-8") will raise an uncaught UnicodeDecodeError if there are non-UTF-8 OpenSSL bag attributes outside the PEM boundaries.

We should extract only the CERTIFICATE blocks before returning, e.g. with a non-greedy r"-----BEGIN CERTIFICATE-----.+?-----END CERTIFICATE-----\r?\n?" (_mtls_helper._CERT_REGEX is greedy and would still grab an interleaved key).

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.

In this PR discovery only resolves cert_path from GOOGLE_API_CERTIFICATE_CONFIG (which on Cloud Run points to the standalone certificates.pem file). Automatic discovery of GKE's combined credentialbundle.pem is not active here.

In our follow-up PR adding GKE support, get_agent_identity_certificate_and_bytes() will be updated to extract only -----BEGIN CERTIFICATE-----...-----END CERTIFICATE----- blocks via non-greedy regex. That strips any private key blocks from combined bundles and discards any non-UTF-8 OpenSSL bag attributes outside the PEM boundaries prior to UTF-8 decoding.

I'll mark this as resolved since it's out of the scope of this PR and will be addressed in the GKE PR.

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.

We should fix this in this PR before merging rather than deferring to the GKE follow-up. GOOGLE_API_CERTIFICATE_CONFIG is not Cloud Run specific. GKE/GCE can set this today with cert_path and key_path pointing to the same combined PEM bundle.

Comment thread packages/google-auth/google/auth/_agent_identity_utils.py
nbayati and others added 7 commits September 17, 2026 21:02
Switch the MDS token acquisition from a GET to a POST request when the agentic cert is detected.

* Add `get_agent_identity_certificate_and_bytes()` utility to read the raw certificate bytes alongside the parsed cert.

* Update `_metadata.get_service_account_token()` (for access tokens) and `IDTokenCredentials.refresh()` (for ID tokens) to send a POST request with the `certificate_chain` payload instead of a GET request when bound tokens are supported.

* Update `_metadata.get()` helper to support `method` and `body` params.

* Add and update unit tests to verify the new POST request flows.
@nbayati nbayati removed the do not merge Indicates a pull request not ready for merge, due to either quality or timing. label Sep 18, 2026
Comment thread packages/google-auth/tests/test_agent_identity_utils.py Outdated
Comment thread packages/google-auth/google/auth/_agent_identity_utils.py Outdated
Comment thread packages/google-auth/google/auth/compute_engine/_metadata.py
Comment thread packages/google-auth/google/auth/_agent_identity_utils.py Outdated
Comment thread packages/google-auth/google/auth/_agent_identity_utils.py Outdated
Comment thread packages/google-auth/google/auth/compute_engine/_metadata.py Outdated
Comment thread packages/google-auth/tests/compute_engine/test_credentials.py Outdated
Comment thread packages/google-auth/tests/compute_engine/test__metadata.py Outdated
) = _agent_identity_utils.get_agent_identity_certificate_and_bytes()

expected_certs = NON_AGENT_IDENTITY_CERT_BYTES + NON_AGENT_IDENTITY_CERT_BYTES
assert isinstance(cert, x509.Certificate)

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.

Can we use AGENT_IDENTITY_CERT_BYTES as the first block and NON_AGENT_IDENTITY_CERT_BYTES as the second in combined_bundle, and assert _is_agent_identity_certificate(cert)? Right now both blocks are NON_AGENT_IDENTITY_CERT_BYTES, so changing return certs[0] to return certs[-1] in parse_certificate still passes the test suite.

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 call, updated!

Comment thread packages/google-auth/google/auth/environment_vars.py Outdated
Comment thread packages/google-auth/google/auth/environment_vars.py Outdated
Comment thread packages/google-auth/tests/test_agent_identity_utils.py Outdated
Comment thread packages/google-auth/google/auth/_agent_identity_utils.py Outdated
Comment thread packages/google-auth/google/auth/transport/_mtls_helper.py Outdated
Comment thread packages/google-auth/google/auth/compute_engine/_metadata.py Outdated
Comment thread packages/google-auth/tests/compute_engine/test__metadata.py Outdated
Comment thread packages/google-auth/tests/transport/test_requests.py Outdated
Comment thread packages/google-auth/google/auth/_agent_identity_utils.py Outdated
Comment thread packages/google-auth/google/auth/_agent_identity_utils.py
Comment thread packages/google-auth/google/auth/_agent_identity_utils.py
"""Returns True only if bound tokens are explicitly disabled via env vars."""
val = os.environ.get(environment_vars.GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN)
if val is not None:
return val.lower() == "false"

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.

Do we need to strip() too to account for whitespace - this may be the case in the fallback check too? Additionally, what about an "" (which isn't None)?

Perhaps this can be simplified to:

val = (
os.environ.get(environment_vars.
GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN)
or os.environ.get(
environment_vars.
GOOGLE_API_PREVENT_AGENT_TOKEN_SHARING_FOR_GCP_SERVICES
)
or "true"
)
return val.strip().lower() == "false"

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.

I kept the if/else branches for readability but added strip() to cover the whitespace scenario. Let me know if you think the simplified version would be better for readability / coding style though.

Comment thread packages/google-auth/google/auth/_agent_identity_utils.py Outdated
Comment thread packages/google-auth/tests/compute_engine/test_credentials.py Outdated
Comment thread packages/google-auth/google/auth/_agent_identity_utils.py Outdated
# Conflicts:
#	packages/google-auth/google/auth/aio/transport/sessions.py
#	packages/google-auth/google/auth/compute_engine/_metadata.py
#	packages/google-auth/tests/compute_engine/test_credentials.py
Comment thread packages/google-auth/tests/test_agent_identity_utils.py
Comment thread packages/google-auth/tests/compute_engine/test__metadata.py Outdated
Comment thread packages/google-auth/google/auth/identity_pool.py
Comment thread packages/google-auth/tests/compute_engine/test_credentials.py Outdated
Comment thread packages/google-auth/google/auth/environment_vars.py Outdated

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

Nits

Comment thread packages/google-auth/tests/test_agent_identity_utils.py Outdated
Comment thread packages/google-auth/tests/test_agent_identity_utils.py Outdated
Comment thread packages/google-auth/google/auth/_agent_identity_utils.py
Comment thread packages/google-auth/tests/test_agent_identity_utils.py Outdated
Comment thread packages/google-auth/google/auth/transport/_mtls_helper.py
@lsirac

lsirac commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Found a few issues and questions while testing the certificate parsing and bound-token changes:

  1. _mtls_helper._read_cert_file and _agent_identity_utils.parse_certificate handle truncated multi-cert files differently: parse_certificate (_agent_identity_utils.py:326-329) checks that len(cert_blocks) matches the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- counts, raising ValueError("Malformed or truncated PEM certificate chain.") if a trailing intermediate cert is truncated. However, _read_cert_file (_mtls_helper.py:539-550) only checks if not cert_match: after re.findall, silently dropping a truncated trailing cert block in a multi-cert file. Also, if a file ends with a truncated header line like -----BEGIN CERT, both functions silently accept the preceding cert without error.

  2. Multi-cert cert_path with identity_pool.Credentials still drops the intermediate cert from subject_token: Now that _mtls_helper._read_cert_file (_mtls_helper.py:539-550) accepts multi-cert bundles in cert_path, identity_pool.Credentials._get_cert_bytes() returns the full PEM chain. However, _X509Supplier.get_subject_token (identity_pool.py:163) still calls x509.load_pem_x509_certificate(leaf_cert_data), which loads only the first PEM block, so subject_token only includes the leaf cert unless trust_chain_path is also configured.

  3. Legacy GOOGLE_API_PREVENT_AGENT_TOKEN_SHARING_FOR_GCP_SERVICES behavior change for non-"false" values: Previously, should_request_bound_token checked val.lower() == "true", so values like "0" or "no" disabled bound tokens. In _is_bound_token_opted_out (_agent_identity_utils.py:231-242), only literal "false" opts out, so "0" or "no" now leave bound tokens enabled.

  4. Bound ID tokens are requested by default while AuthorizedSession and AuthorizedHttp default to plain TLS: IDTokenCredentials._call_metadata_identity_endpoint (compute_engine/credentials.py:520-534) now calls _metadata._build_token_request_options (_metadata.py:484-507), which switches to POST with certificate_chain whenever an Agent Identity cert is configured unless GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN=false. Since AuthorizedSession (requests.py:415) and AuthorizedHttp (urllib3.py:310) default to _is_mtls = False unless configure_mtls_channel() is called, and fetch_id_token() callers often target standard HTTPS endpoints, will callers using bound ID tokens over plain TLS fail downstream validation without a per-credential opt-out?

  5. No fallback to GET on POST failure, and POST is sent for explicit service account emails: get_service_account_token (_metadata.py:534-541) selects method="POST" before building instance/service-accounts/{service_account}/token, and _metadata.get (_metadata.py:250-341) only retries the same HTTP method on retryable status codes. Should POST be restricted to service_account == "default", or fall back to GET if an MDS endpoint or emulator rejects POST?

@nbayati

nbayati commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor Author

2. Multi-cert cert_path with identity_pool.Credentials still drops the intermediate cert from subject_token: Now that _mtls_helper._read_cert_file (_mtls_helper.py:539-550) accepts multi-cert bundles in cert_path, identity_pool.Credentials._get_cert_bytes() returns the full PEM chain. However, _X509Supplier.get_subject_token (identity_pool.py:163) still calls x509.load_pem_x509_certificate(leaf_cert_data), which loads only the first PEM block, so subject_token only includes the leaf cert unless trust_chain_path is also configured.

_X509Supplier.get_subject_token wasn't modified in this PR. On main, it already loads only the leaf certificate from cert_path (x509.load_pem_x509_certificate) and reads intermediate certificates from trust_chain_path. However, the described behavior is accurate, and is working as intended and matches the behavior across the other SDKs (Go, Java, etc.). Based on the X.509 WIF documentation and gcloud iam workload-identity-pools create-cred-config (--credential-cert-trust-chain-path), cert_path only supplies the leaf certificate for subject_token, while trust_chain_path must be set whenever intermediate CAs need to be sent in the token. When both point to the same combined bundle (such as certificates.pem), identity_pool.py already deduplicates the leading leaf certificate from the trust chain and appends the remaining intermediates.

Automatically appending trailing certificates from cert_path when trust_chain_path is omitted would diverge from the other SDKs and could push the combined certificate count across subject_token and TrustStore.intermediateCas over STS's 10-certificate pre-deduplication limit when intermediates are already configured in the pool's trust store.

@nbayati

nbayati commented Sep 23, 2026

Copy link
Copy Markdown
Contributor Author
  1. _mtls_helper._read_cert_file and _agent_identity_utils.parse_certificate handle truncated multi-cert files differently: parse_certificate (_agent_identity_utils.py:326-329) checks that len(cert_blocks) matches the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- counts, raising ValueError("Malformed or truncated PEM certificate chain.") if a trailing intermediate cert is truncated. However, _read_cert_file (_mtls_helper.py:539-550) only checks if not cert_match: after re.findall, silently dropping a truncated trailing cert block in a multi-cert file. Also, if a file ends with a truncated header line like -----BEGIN CERT, both functions silently accept the preceding cert without error.

Addressed the first part of the comment:
_has_unmatched_pem_markers in _mtls_helper.py is now shared by _read_cert_file and parse_certificate so both functions reject unmatched -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- blocks consistently.

However, the check remains scoped to full -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- delimiters rather than partial prefixes like -----BEGIN CERT. Under RFC 7468 Section 2, as well as in OpenSSL's SSLContext.load_cert_chain and cryptography.x509.load_pem_x509_certificates, only complete -----BEGIN/END <LABEL>----- lines mark encapsulation boundaries. Matching shorter dash or prefix substrings still misses earlier byte-offset truncations such as -----BEGIN CE or ---, while risking false positives on valid PEM comments and pre-encapsulation headers.

@nbayati

nbayati commented Sep 23, 2026

Copy link
Copy Markdown
Contributor Author

3. Legacy GOOGLE_API_PREVENT_AGENT_TOKEN_SHARING_FOR_GCP_SERVICES behavior change for non-"false" values: Previously, should_request_bound_token checked val.lower() == "true", so values like "0" or "no" disabled bound tokens. In _is_bound_token_opted_out (_agent_identity_utils.py:231-242), only literal "false" opts out, so "0" or "no" now leave bound tokens enabled.

This is intentional and matches the documented contract for GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN in environment_vars.py ("Defaults to enabled; only a case-insensitive 'false' disables it"). In _is_bound_token_opted_out(), an empty string "" is treated as unset so it falls back to GOOGLE_API_PREVENT_AGENT_TOKEN_SHARING_FOR_GCP_SERVICES (and defaults to enabled) rather than silently disabling token binding when an empty env var template is passed.

4. Bound ID tokens are requested by default while AuthorizedSession and AuthorizedHttp default to plain TLS: IDTokenCredentials._call_metadata_identity_endpoint (compute_engine/credentials.py:520-534) now calls _metadata._build_token_request_options (_metadata.py:484-507), which switches to POST with certificate_chain whenever an Agent Identity cert is configured unless GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN=false. Since AuthorizedSession (requests.py:415) and AuthorizedHttp (urllib3.py:310) default to _is_mtls = False unless configure_mtls_channel() is called, and fetch_id_token() callers often target standard HTTPS endpoints, will callers using bound ID tokens over plain TLS fail downstream validation without a per-credential opt-out?

_build_token_request_options only requests bound tokens when GOOGLE_API_CERTIFICATE_CONFIG provides an Agent Identity SPIFFE certificate (matching the existing bindCertificateFingerprint behavior on main). AuthorizedSession and AuthorizedHttp are intentionally designed to require an explicit configure_mtls_channel() call to enable mTLS (which Google Cloud client libraries call automatically when workload certificates are present).

5. No fallback to GET on POST failure, and POST is sent for explicit service account emails: get_service_account_token (_metadata.py:534-541) selects method="POST" before building instance/service-accounts/{service_account}/token, and _metadata.get (_metadata.py:250-341) only retries the same HTTP method on retryable status codes. Should POST be restricted to service_account == "default", or fall back to GET if an MDS endpoint or emulator rejects POST?

POST is only used when GOOGLE_API_CERTIFICATE_CONFIG provides an Agent Identity SPIFFE certificate (and compute_engine.Credentials already passes service_account="default"), so non-Agent service accounts will continue to use GET. When an Agent Identity certificate is active and bound tokens are enabled, failing closed rather than falling back to GET is intentional so transient errors or misconfigurations cannot silently downgrade a bound token request to an unbound bearer token (environments that need unbound tokens can opt out via GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN=false).

@nbayati
nbayati requested a review from lsirac September 23, 2026 05:44
@lsirac

lsirac commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Thanks Negar!

  • Is there a way to opt out of binding on IDTokenCredentials / fetch_id_token() without setting GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN=false globally and disabling bound access tokens for everything?
  • On 4. With an Agent Identity cert present, fetch_id_token() now requests a bound ID token. Does a caller that sends it over plain TLS to a standard *.run.app URL still pass validation, or does it need *.mtls.run.app with configure_mtls_channel()?

@lsirac

lsirac commented Sep 23, 2026

Copy link
Copy Markdown
Contributor

There is one edge case left in the multi-certificate PEM chain parsing added in this PR. The check for unmatched certificate markers was added when reading certificate files and parsing certificates, but not when reading the output of a certificate provider command. If a certificate provider command outputs a valid leaf certificate followed by a truncated intermediate certificate, the regex match strips out the incomplete trailing block and returns a single-certificate chain without raising a client certificate error. Running that same unmatched marker check on the certificate provider command output before joining the chain will keep both paths consistent.

@nbayati
nbayati changed the base branch from main to cr-bound-id-token September 27, 2026 06:13
@nbayati

nbayati commented Sep 27, 2026

Copy link
Copy Markdown
Contributor Author
  • Is there a way to opt out of binding on IDTokenCredentials / fetch_id_token() without setting GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN=false globally and disabling bound access tokens for everything?

No, we intentionally kept a single control (GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN) for both bound access and ID tokens rather than introducing separate knobs.

  • On 4. With an Agent Identity cert present, fetch_id_token() now requests a bound ID token. Does a caller that sends it over plain TLS to a standard *.run.app URL still pass validation, or does it need *.mtls.run.app with configure_mtls_channel()?

Third-party OIDC verifiers ignore the cnf claim over plain TLS, but Cloud Run IAM enforces cnf whenever the claim is present in the token. Because standard *.run.app endpoints do not request a client certificate during the TLS handshake, calling an IAM-protected Cloud Run service requires either calling its *.<region>.mtls.run.app URL with configure_mtls_channel() or sending an unbound token. We are working with the CR team to re-evaluate the default behavior and address it in a follow up PR.

@nbayati
nbayati merged commit 622b51c into googleapis:cr-bound-id-token Sep 27, 2026
111 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.

4 participants