Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 57269d5

Browse filesBrowse files
fix(auth): configure mTLS for impersonated credentials (#17404)
### Description This PR configures `AuthorizedSession` to support mutual TLS (mTLS) when refreshing impersonated ID tokens or signing bytes. ### Context When using impersonated credentials (e.g., via `gcloud auth print-identity-token --impersonate-service-account=...`) in environments where mTLS is enforced by Context Aware Access (CAA) policies, the requests fail with `401 UNAUTHENTICATED` (specifically `ACCESS_TOKEN_TYPE_UNSUPPORTED`). Although the endpoint correctly resolves to the mTLS domain (`iamcredentials.mtls.googleapis.com`), the underlying `AuthorizedSession` created in `impersonated_credentials.py` is never configured with the client certificate, causing the TLS handshake to lack the required client cert. ### Changes * **`google/auth/impersonated_credentials.py`**: * Added `authed_session.configure_mtls_channel()` in `Credentials.sign_bytes` right after the session is created. * Added `authed_session.configure_mtls_channel()` in `IDTokenCredentials.refresh` right after the session is created. * **`tests/test_impersonated_credentials.py`**: * Added `test_sign_bytes_configures_mtls` and `test_id_token_refresh_configures_mtls` unit tests to verify `configure_mtls_channel` is invoked. --------- Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> Co-authored-by: amtk3 <254821816+amtk3@users.noreply.github.com>
1 parent 59fe7cf commit 57269d5
Copy full SHA for 57269d5

2 files changed

+45Lines changed: 45 additions & 0 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎packages/google-auth/google/auth/impersonated_credentials.py‎

Copy file name to clipboardExpand all lines: packages/google-auth/google/auth/impersonated_credentials.py
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,7 @@ def sign_bytes(self, message):
388388
headers = {"Content-Type": "application/json"}
389389

390390
authed_session = AuthorizedSession(self._source_credentials)
391+
authed_session.configure_mtls_channel()
391392

392393
try:
393394
retries = _exponential_backoff.ExponentialBackoff()
@@ -627,6 +628,7 @@ def refresh(self, request):
627628
authed_session = AuthorizedSession(
628629
self._target_credentials._source_credentials, auth_request=request
629630
)
631+
authed_session.configure_mtls_channel()
630632

631633
try:
632634
response = authed_session.post(
Collapse file

‎packages/google-auth/tests/test_impersonated_credentials.py‎

Copy file name to clipboardExpand all lines: packages/google-auth/tests/test_impersonated_credentials.py
+43Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -639,6 +639,26 @@ def _sign_bytes_helper(
639639

640640
assert signature == b"signature"
641641

642+
@mock.patch(
643+
"google.auth.transport.requests.AuthorizedSession.configure_mtls_channel",
644+
autospec=True,
645+
)
646+
def test_sign_bytes_configures_mtls(
647+
self, mock_configure_mtls, mock_donor_credentials, mock_authorizedsession_sign
648+
):
649+
credentials = self.make_credentials(lifetime=None)
650+
# Refresh is needed to make credentials valid before signing
651+
request = self.make_request(
652+
data=json.dumps(
653+
{"accessToken": "token", "expireTime": "2026-06-09T00:00:00Z"}
654+
),
655+
status=http_client.OK,
656+
)
657+
credentials.refresh(request)
658+
659+
credentials.sign_bytes(b"signed bytes")
660+
mock_configure_mtls.assert_called_once()
661+
642662
def test_sign_bytes_failure(self):
643663
credentials = self.make_credentials(lifetime=None)
644664

@@ -751,6 +771,29 @@ def test_with_scopes_provide_default_scopes(self):
751771
)
752772
assert credentials._target_scopes == ["fake_scope1"]
753773

774+
@mock.patch(
775+
"google.auth.transport.requests.AuthorizedSession.configure_mtls_channel",
776+
autospec=True,
777+
)
778+
def test_id_token_refresh_configures_mtls(
779+
self, mock_configure_mtls, mock_donor_credentials
780+
):
781+
credentials = self.make_credentials(lifetime=None)
782+
credentials.token = "token"
783+
id_creds = impersonated_credentials.IDTokenCredentials(
784+
credentials, target_audience="https://foo.bar"
785+
)
786+
787+
with mock.patch(
788+
"google.auth.transport.requests.AuthorizedSession.post", autospec=True
789+
) as mock_post:
790+
mock_post.return_value = MockResponse(
791+
{"token": ID_TOKEN_DATA}, http_client.OK
792+
)
793+
id_creds.refresh(None)
794+
795+
mock_configure_mtls.assert_called_once()
796+
754797
def test_id_token_success(
755798
self, mock_donor_credentials, mock_authorizedsession_idtoken
756799
):

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.