Skip to content

Commit 2150dac

Browse files
feat: add GCP Application Default Credentials token strategy
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent b78e0b6 commit 2150dac

2 files changed

Lines changed: 33 additions & 1 deletion

File tree

cloudsmith_cli/core/credentials/oidc/detectors/gcp.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,4 +127,15 @@ def _token_from_service_account_file(self, audience: str) -> str | None:
127127
return None
128128

129129
def _token_from_adc(self, audience: str) -> str | None:
130-
return None
130+
try:
131+
import google.auth # pylint: disable=import-error
132+
from google.auth.transport.requests import ( # pylint: disable=import-error
133+
Request,
134+
)
135+
136+
credentials, _ = google.auth.default()
137+
credentials.refresh(Request())
138+
return getattr(credentials, "id_token", None) or None
139+
except Exception: # pylint: disable=broad-exception-caught
140+
logger.debug("GCPDetector: ADC id_token fetch failed", exc_info=True)
141+
return None

cloudsmith_cli/core/tests/test_gcp_detector.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,24 @@ def test_skipped_when_env_unset(self):
138138
get.side_effect = requests.exceptions.ConnectionError()
139139
with pytest.raises(ValueError):
140140
make_detector().get_token()
141+
142+
143+
class TestGetTokenADC:
144+
def test_returns_id_token_from_adc(self):
145+
fake_creds = mock.Mock(id_token="adc-jwt")
146+
with mock.patch(f"{GCP_MODULE}.requests.get") as get, mock.patch.dict(
147+
"os.environ", {}, clear=True
148+
), mock.patch("google.auth.default", return_value=(fake_creds, "proj")):
149+
get.side_effect = requests.exceptions.ConnectionError()
150+
token = make_detector().get_token()
151+
assert token == "adc-jwt"
152+
fake_creds.refresh.assert_called_once()
153+
154+
def test_raises_when_adc_has_no_id_token(self):
155+
fake_creds = mock.Mock(spec=["refresh"]) # no id_token attribute
156+
with mock.patch(f"{GCP_MODULE}.requests.get") as get, mock.patch.dict(
157+
"os.environ", {}, clear=True
158+
), mock.patch("google.auth.default", return_value=(fake_creds, "proj")):
159+
get.side_effect = requests.exceptions.ConnectionError()
160+
with pytest.raises(ValueError):
161+
make_detector().get_token()

0 commit comments

Comments
 (0)