The runner asks Forgejo for a job-scoped OIDC token, naming the GCP provider as its audience. Forgejo signs it, and its claims say exactly which workflow run it came from: no long-lived credential anywhere.
Call sequence
curl 'https://forgejo.authlab.app/api/actions/_apis/pipelines/workflows/40/idtoken?placeholder=true&audience=//iam.googleapis.com/projects/1234567890/locations/global/workloadIdentityPools/lab-pool/providers/ci-provider' \
-H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" { "value": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjlZUDA2bjJvMTd6c3NZMWVBRkFoOUhoYjdTUnhqbGdhWmNmLUdiN2xmaU0iLCJ0eXAiOiJKV1QifQ.eyJhY3RvciI6ImF1dGhsYWIiLCJhdWQiOiIvL2lhbS5nb29nbGVhcGlzLmNvbS9wcm9qZWN0cy8xMjM0NTY3ODkwL2xvY2F0aW9ucy9nbG9iYWwvd29ya2xvYWRJZGVudGl0eVBvb2xzL2xhYi1wb29sL3Byb3ZpZGVycy9jaS1wcm92aWRlciIsImJhc2VfcmVmIjoiIiwiZXZlbnRfbmFtZSI6….anonymized-signature-not-valid-for-cryptographic-use" }
origin · the workflow
# .forgejo/workflows/capture.yml
on: push
permissions:
id-token: write # lets the job request an OIDC token
jobs:
capture:
steps:
- name: Mint OIDC token
with:
audience: //iam.googleapis.com/projects/1234567890/locations/global/workloadIdentityPools/lab-pool/providers/ci-provider
the audience you request becomes the
token's aud
Header
algRS256kid9YP06n2o17zssY1eAFAh9Hhb7SRxjlgaZcf-Gb7lfiMtypJWT
Payload
| Claim | Value |
|---|---|
actor | authlab |
aud | //iam.googleapis.com/projects/1234567890/locations/global/workloadIdentityPools/lab-pool/providers/ci-provider |
base_ref | |
event_name | push |
exp | 1778622390 |
head_ref | |
iat | 1778618790 |
iss | https://forgejo.authlab.app/api/actions |
nbf | 1778618790 |
ref | refs/heads/main |
ref_protected | false |
ref_type | branch |
repository | authlab/artifacts-visualizer |
repository_owner | authlab |
run_attempt | 1 |
run_id | 40 |
run_number | 16 |
sha | 14c23450c8322e4c8e315e68d4241d184826be06 |
sub | repo:authlab/artifacts-visualizer:ref:refs/heads/main |
workflow | capture.yml |
workflow_ref | authlab/artifacts-visualizer/.forgejo/workflows/capture.yml@refs/heads/main |
Compact Serialization
eyJhbGciOiJSUzI1NiIsImtpZCI6IjlZUDA2bjJvMTd6c3NZMWVBRkFoOUhoYjdTUnhqbGdhWmNmLUdiN2xmaU0iLCJ0eXAiOiJKV1QifQeyJhY3RvciI6ImF1dGhsYWIiLCJhdWQiOiIvL2lhbS5nb29nbGVhcGlzLmNvbS9wcm9qZWN0cy8xMjM0NTY3ODkwL2xvY2F0aW9ucy9nbG9iYWwvd29ya2xvYWRJZGVudGl0eVBvb2xzL2xhYi1wb29sL3Byb3ZpZGVycy9jaS1wcm92aWRlciIsImJhc2VfcmVmIjoiIiwiZXZlbnRfbmFtZSI6InB1c2giLCJleHAiOjE3Nzg2MjIzOTAsImhlYWRfcmVmIjoiIiwiaWF0IjoxNzc4NjE4NzkwLCJpc3MiOiJodHRwczovL2Zvcmdlam8uYXV0aGxhYi5hcHAvYXBpL2FjdGlvbnMiLCJuYmYiOjE3Nzg2MTg3OTAsInJlZiI6InJlZnMvaGVhZHMvbWFpbiIsInJlZl9wcm90ZWN0ZWQiOiJmYWxzZSIsInJlZl90eXBlIjoiYnJhbmNoIiwicmVwb3NpdG9yeSI6ImF1dGhsYWIvYXJ0aWZhY3RzLXZpc3VhbGl6ZXIiLCJyZXBvc2l0b3J5X293bmVyIjoiYXV0aGxhYiIsInJ1bl9hdHRlbXB0IjoiMSIsInJ1bl9pZCI6IjQwIiwicnVuX251bWJlciI6IjE2Iiwic2hhIjoiMTRjMjM0NTBjODMyMmU0YzhlMzE1ZTY4ZDQyNDFkMTg0ODI2YmUwNiIsInN1YiI6InJlcG86YXV0aGxhYi9hcnRpZmFjdHMtdmlzdWFsaXplcjpyZWY6cmVmcy9oZWFkcy9tYWluIiwid29ya2Zsb3ciOiJjYXB0dXJlLnltbCIsIndvcmtmbG93X3JlZiI6ImF1dGhsYWIvYXJ0aWZhY3RzLXZpc3VhbGl6ZXIvLmZvcmdlam8vd29ya2Zsb3dzL2NhcHR1cmUueW1sQHJlZnMvaGVhZHMvbWFpbiJ9anonymized-signature-not-valid-for-cryptographic-use