Before any cloud credential exchange happens, the CI job must first obtain a signed OIDC token from its own platform. This minting step is invisible in most tutorials but is the first failure point. On GitHub Actions the runner exposes two job-scoped environment variables — they exist only for the run, never stored: ``` ACTIONS_ID_TOKEN_REQUEST_URL # endpoint to request the token ACTIONS_ID_TOKEN_REQUEST_TOKEN # bearer authorizing that request ``` The job must declare the permission `id-token: write`. Without it the platform refuses to mint a token, and the later assume-role fails with an opaque credentials error — the first thing to check on a 403. Requesting the token: ```bash curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \ "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=sts.amazonaws.com" ``` The **`audience`** parameter the caller passes becomes the JWT's `aud` claim. It binds the token to one intended verifier, so a token minted for AWS cannot be replayed against a different cloud. The returned JWT carries `iss`, `sub`, `aud`, `exp` signed by the platform's private key — provable to any verifier holding the published JWKS. Only after this token exists does the cloud-side exchange in [[Workload Identity Federation]] begin. ## Sources - [GitHub Docs — OpenID Connect reference](https://docs.github.com/actions/reference/openid-connect-reference) --- *Source: [[Workload Identity Federation]]*