Closed
Description
TL;DR
I'm trying to get an ID token to use for authenticating towards Google Cloud Run services in GitHub Actions in Python code, however, for some reason it does not seem to be working.
Expected behavior
Receive the ID token.
Observed behavior
I got the following error:
...
credentials.refresh(request=Request()) # type: ignore[no-untyped-call]
.../lib/python3.8/site-packages/google/auth/external_account.py:397: in refresh
self._impersonated_credentials.refresh(request)
.../lib/python3.8/site-packages/google/auth/impersonated_credentials.py:250: in refresh
self._update_token(request)
.../lib/python3.8/site-packages/google/auth/impersonated_credentials.py:282: in _update_token
self.token, self.expiry = _make_iam_token_request(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
request = <google.auth.transport.requests.Request object at ...>
principal = 'app-tester@....iam.gserviceaccount.com'
headers = {'Content-Type': 'application/json', 'authorization': '*** 'x-allowed-locations': '0x0', 'x-goog-api-client': 'gl-python/3.8.18 auth/2.29.0 auth-request-type/at cred-type/imp'}
body = b'{"delegates": null, "scope": null, "lifetime": "3600s"}'
iam_endpoint_override = 'https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/app-tester@....iam.gserviceaccount.com:generateAccessToken'
def _make_iam_token_request(
request, principal, headers, body, iam_endpoint_override=None
):
"""Makes a request to the Google Cloud IAM service for an access token.
Args:
request (Request): The Request object to use.
principal (str): The principal to request an access token for.
headers (Mapping[str, str]): Map of headers to transmit.
body (Mapping[str, str]): JSON Payload body for the iamcredentials
API call.
iam_endpoint_override (Optiona[str]): The full IAM endpoint override
with the target_principal embedded. This is useful when supporting
impersonation with regional endpoints.
Raises:
google.auth.exceptions.TransportError: Raised if there is an underlying
HTTP connection error
google.auth.exceptions.RefreshError: Raised if the impersonated
credentials are not available. Common reasons are
`iamcredentials.googleapis.com` is not enabled or the
`Service Account Token Creator` is not assigned
"""
iam_endpoint = iam_endpoint_override or _IAM_ENDPOINT.format(principal)
body = json.dumps(body).encode("utf-8")
response = request(url=iam_endpoint, method="POST", headers=headers, body=body)
# support both string and bytes type response.data
response_body = (
response.data.decode("utf-8")
if hasattr(response.data, "decode")
else response.data
)
if response.status != http_client.OK:
> raise exceptions.RefreshError(_REFRESH_ERROR, response_body)
E google.auth.exceptions.RefreshError: ('Unable to acquire impersonated credentials', '{\n "error": {\n "code": 400,\n "message": "Request contains an invalid argument.",\n "status": "INVALID_ARGUMENT"\n }\n}\n')
.../lib/python3.8/site-packages/google/auth/impersonated_credentials.py:100: RefreshError
Action YAML
See https://github.com/logikal-io/github-workflows/blob/main/.github/workflows/run-python-tests.yml
It is essentially:
- name: Authenticate to Google Cloud Platform
uses: google-github-actions/auth@v2
with:
workload_identity_provider: ${{ inputs.gcp-testing-workload-identity-provider }}
service_account: ${{ inputs.gcp-testing-service-account }}
- name: Run pytest
run: orb --command 'pytest ${{ inputs.pytest-options }}'
Log output
------------------------------ Captured log call -------------------------------
2024-09-12 17:06:04.630 DEBUG Loading default credentials (stormware.google.auth:91)
2024-09-12 17:06:04.630 DEBUG Checking /home/runner/work/.../gha-creds-....json for explicit credentials as part of auth process... (google.auth._default:255)
2024-09-12 17:06:04.631 DEBUG Making request: GET https://pipelinesghubeus7.actions.githubusercontent.com/.../_apis/distributedtask/hubs/Actions/plans.../jobs/.../idtoken?api-version=2.0&audience=https%3A%2F%2Fiam.googleapis.com%2Fprojects%2...%2Flocations%2Fglobal%2FworkloadIdentityPools%2Fci-cd%2Fproviders%2Fgithub-actions (google.auth.transport.requests:185)
2024-09-12 17:06:04.920 DEBUG Making request: POST https://sts.googleapis.com/v1/token (google.auth.transport.requests:185)
2024-09-12 17:06:04.977 DEBUG Making request: POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/app-tester@....iam.gserviceaccount.com:generateAccessToken (google.auth.transport.requests:185)
Additional information
The following code snippet was triggering the error:
from google.auth import default
from google.auth.transport.requests import Request
credentials = default()[0]
credentials.refresh(request=Request()) # <-- here
print(credentials.id_token)
I understand that there is a way to generate an ID token as an output value, however, injecting that value into the Python script seems like a hack as opposed to using the default credential flow. Shouldn't this approach work too?