Skip to content

Commit 1aa1cd4

Browse files
author
Abhi Srivastava
committed
docs: Add troubleshooting for Python ID token refresh error
This addresses issue #441 where users encounter an error when trying to refresh credentials to retrieve an ID token in Python code. The error occurs because the Google Auth library requires scopes to be set when refreshing credentials for impersonation. Changes: - Added new troubleshooting section explaining the ID token refresh error - Provided solution with code example showing how to add scopes before refreshing - Added Python usage note in README.md with quick reference - Enhanced examples with Python code demonstrating proper credential usage Fixes #441
1 parent ba79af0 commit 1aa1cd4

File tree

3 files changed

+124
-0
lines changed

3 files changed

+124
-0
lines changed

README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,29 @@ regardless of the authentication mechanism.
322322
"token_format" is "id_token".
323323
324324
325+
## Python Usage Note
326+
327+
When using Workload Identity Federation with Python libraries (e.g., `google-auth`), you may encounter errors when trying to refresh credentials to get an ID token. This is because the Google Auth library requires scopes to be set when refreshing credentials for impersonation.
328+
329+
If you need an ID token in Python, you have two options:
330+
331+
1. **Use the `token_format` parameter** (recommended): Generate the ID token directly with this action and use it as an environment variable in your Python code.
332+
333+
2. **Add scopes before refreshing**: If using default credentials, add the required scopes before refreshing:
334+
335+
```python
336+
from google.auth import default
337+
from google.auth.transport.requests import Request
338+
339+
credentials, project = default()
340+
credentials = credentials.with_scopes(
341+
["https://www.googleapis.com/auth/cloud-platform"]
342+
)
343+
credentials.refresh(request=Request())
344+
```
345+
346+
For more details and examples, see the [Troubleshooting guide](docs/TROUBLESHOOTING.md#cannot-refresh-credentials-to-retrieve-an-id-token) and [Examples](docs/EXAMPLES.md#using-default-credentials-with-scopes-in-python).
347+
325348

326349
<a id="setup"></a>
327350
## Setup

docs/EXAMPLES.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,68 @@ jobs:
187187
run: |-
188188
curl https://myapp-uvehjacqzq.a.run.app \
189189
--header "Authorization: Bearer ${{ steps.auth.outputs.id_token }}"
190+
191+
# Example of using ID token in Python code
192+
- id: 'python-example'
193+
run: |-
194+
python -c "
195+
import os
196+
import requests
197+
198+
# ID token is available as environment variable
199+
id_token = os.environ.get('GOOGLE_ID_TOKEN', '${{ steps.auth.outputs.id_token }}')
200+
201+
# Use the token to invoke a Cloud Run service
202+
response = requests.get(
203+
'https://myapp-uvehjacqzq.a.run.app',
204+
headers={'Authorization': f'Bearer {id_token}'}
205+
)
206+
print(response.text)
207+
"
208+
```
209+
210+
### Using Default Credentials with Scopes in Python
211+
212+
When using Workload Identity Federation with Python libraries, you may need to add scopes before refreshing credentials:
213+
214+
```yaml
215+
jobs:
216+
job_id:
217+
permissions:
218+
contents: 'read'
219+
id-token: 'write'
220+
221+
steps:
222+
- uses: 'actions/checkout@v4'
223+
224+
- id: 'auth'
225+
uses: 'google-github-actions/auth@v2'
226+
with:
227+
workload_identity_provider: 'projects/123456789/locations/global/workloadIdentityPools/my-pool/providers/my-provider'
228+
service_account: '[email protected]'
229+
230+
- id: 'python-auth'
231+
run: |-
232+
python -c "
233+
from google.auth import default
234+
from google.auth.transport.requests import Request
235+
236+
# Get default credentials
237+
credentials, project = default()
238+
239+
# Add scopes before refreshing for impersonation
240+
credentials = credentials.with_scopes(
241+
['https://www.googleapis.com/auth/cloud-platform']
242+
)
243+
244+
# Refresh to get the token
245+
credentials.refresh(request=Request())
246+
247+
# Now you can use the credentials
248+
print(f'Access token: {credentials.token}')
249+
if hasattr(credentials, 'id_token'):
250+
print(f'ID token: {credentials.id_token}')
251+
"
190252
```
191253

192254
[github-markdown-toc]: https://github.blog/changelog/2021-04-13-table-of-contents-support-in-markdown-files/

docs/TROUBLESHOOTING.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,45 @@ tool like `jq`:
230230
cat credentials.json | jq -r tostring
231231
```
232232

233+
## Cannot refresh credentials to retrieve an ID token
234+
235+
If you get an error like:
236+
237+
```text
238+
google.auth.exceptions.RefreshError: ('Unable to acquire impersonated credentials', '{"error": {"code": 400, "message": "Request contains an invalid argument.", "status": "INVALID_ARGUMENT"}}')
239+
```
240+
241+
when trying to refresh credentials in Python code to get an ID token, this is usually because the credentials are missing required scopes. The Google Auth library requires scopes to be set when refreshing credentials for impersonation.
242+
243+
To fix this issue, add the required scopes before refreshing:
244+
245+
```python
246+
from google.auth import default
247+
from google.auth.transport.requests import Request
248+
249+
credentials, project = default()
250+
# Add scopes before refreshing
251+
credentials = credentials.with_scopes(
252+
["https://www.googleapis.com/auth/cloud-platform"]
253+
)
254+
credentials.refresh(request=Request())
255+
# Now you can access the ID token
256+
print(credentials.id_token)
257+
```
258+
259+
Alternatively, you can use the `token_format` parameter of this action to generate an ID token directly:
260+
261+
```yaml
262+
- uses: 'google-github-actions/auth@v2'
263+
with:
264+
workload_identity_provider: ${{ secrets.WIF_PROVIDER }}
265+
service_account: ${{ secrets.WIF_SERVICE_ACCOUNT }}
266+
token_format: 'id_token'
267+
id_token_audience: 'https://example.com'
268+
```
269+
270+
This will export the ID token as an environment variable that you can use in your Python code.
271+
233272
## Organizational Policy Constraints
234273

235274
> **ℹ️ NOTE!** Your Google Cloud organization administrator controls these

0 commit comments

Comments
 (0)