Skip to content
This repository was archived by the owner on Mar 6, 2026. It is now read-only.

Commit 1123f87

Browse files
fix: raise RefreshError for missing token in impersonated credentials
Instead of crashing with a KeyError when the ID token is missing from the response (even if the status code is 200), raise a proper RefreshError. Fixes #1167
1 parent 0e28e6f commit 1123f87

2 files changed

Lines changed: 85 additions & 1 deletion

File tree

google/auth/impersonated_credentials.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -640,7 +640,14 @@ def refresh(self, request):
640640
"Error getting ID token: {}".format(response.json())
641641
)
642642

643-
id_token = response.json()["token"]
643+
try:
644+
id_token = response.json()["token"]
645+
except (KeyError, ValueError) as caught_exc:
646+
new_exc = exceptions.RefreshError(
647+
"No ID token in response.", response.json()
648+
)
649+
raise new_exc from caught_exc
650+
644651
self.token = id_token
645652
self.expiry = datetime.utcfromtimestamp(
646653
jwt.decode(id_token, verify=False)["exp"]

tests/test_issue_1167.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import unittest
2+
from unittest.mock import MagicMock, patch
3+
import json
4+
import http.client as http_client
5+
from google.auth import exceptions
6+
from google.auth import impersonated_credentials
7+
from google.auth import credentials
8+
9+
class TestIDTokenCredentialsFix(unittest.TestCase):
10+
def test_refresh_200_missing_token_raises_refresh_error(self):
11+
# Setup
12+
mock_source_creds = MagicMock(spec=credentials.Credentials)
13+
mock_target_creds = MagicMock(spec=impersonated_credentials.Credentials)
14+
mock_target_creds._source_credentials = mock_source_creds
15+
mock_target_creds.universe_domain = "googleapis.com"
16+
mock_target_creds.signer_email = "signer@example.com"
17+
mock_target_creds._delegates = []
18+
19+
creds = impersonated_credentials.IDTokenCredentials(
20+
target_credentials=mock_target_creds,
21+
target_audience="aud",
22+
include_email=True
23+
)
24+
25+
# Mock AuthorizedSession
26+
with patch("google.auth.transport.requests.AuthorizedSession") as MockSession:
27+
mock_session_instance = MockSession.return_value
28+
29+
# Mock Response 200 but missing token
30+
mock_response = MagicMock()
31+
mock_response.status_code = 200
32+
mock_response.json.return_value = {"not_token": "something"}
33+
mock_session_instance.post.return_value = mock_response
34+
35+
request = MagicMock()
36+
37+
# Action & Assert
38+
# Before the fix, this raised KeyError. Now it should raise RefreshError.
39+
with self.assertRaises(exceptions.RefreshError) as cm:
40+
creds.refresh(request)
41+
42+
self.assertIn("No ID token in response", str(cm.exception))
43+
44+
def test_refresh_403_raises_refresh_error(self):
45+
# Setup (same as before to ensure no regression)
46+
mock_source_creds = MagicMock(spec=credentials.Credentials)
47+
mock_target_creds = MagicMock(spec=impersonated_credentials.Credentials)
48+
mock_target_creds._source_credentials = mock_source_creds
49+
mock_target_creds.universe_domain = "googleapis.com"
50+
mock_target_creds.signer_email = "signer@example.com"
51+
mock_target_creds._delegates = []
52+
53+
creds = impersonated_credentials.IDTokenCredentials(
54+
target_credentials=mock_target_creds,
55+
target_audience="aud",
56+
include_email=True
57+
)
58+
59+
# Mock AuthorizedSession
60+
with patch("google.auth.transport.requests.AuthorizedSession") as MockSession:
61+
mock_session_instance = MockSession.return_value
62+
63+
# Mock Response 403
64+
mock_response = MagicMock()
65+
mock_response.status_code = 403
66+
mock_response.json.return_value = {"error": "Permission denied"}
67+
mock_session_instance.post.return_value = mock_response
68+
69+
request = MagicMock()
70+
71+
with self.assertRaises(exceptions.RefreshError) as cm:
72+
creds.refresh(request)
73+
74+
self.assertIn("Error getting ID token", str(cm.exception))
75+
76+
if __name__ == "__main__":
77+
unittest.main()

0 commit comments

Comments
 (0)