Long-lived cloud keys in secrets are the most common credential leak; OIDC removes them entirely.
With OpenID Connect, a job asks GitHub for a signed JSON Web Token describing itself: repository, ref, environment, workflow and more. Your cloud provider is configured to trust GitHub's token issuer (https://token.actions.githubusercontent.com) and exchanges a valid token for short-lived cloud credentials. No access keys are stored in GitHub at all.
To use it:
- Grant the job
permissions: id-token: write(pluscontents: readfor checkout). - In the cloud, create a trust relationship: an AWS IAM OIDC provider and role, an Azure federated credential, or a GCP workload identity pool.
- Use the provider's login action, such as
aws-actions/configure-aws-credentialswithrole-to-assume.
The security lives in the trust policy conditions on the token's claims. The key claim is sub, for example repo:my-org/shop:environment:production or repo:my-org/shop:ref:refs/heads/main. Always restrict sub to your repository and a specific branch or environment; a policy that only checks the audience would let any GitHub repository assume your role.
Credentials typically last an hour and die with the job, so a leaked log or compromised dependency gets far less than a permanent key would give.
Example
permissions:
id-token: write # allow requesting the OIDC token
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v7
- uses: aws-actions/configure-aws-credentials@v6
with:
role-to-assume: arn:aws:iam::123456789012:role/gha-deploy
aws-region: eu-central-1
- run: aws sts get-caller-identity # assumed-role/gha-deploy/...
# AWS trust policy condition (excerpt):
# "token.actions.githubusercontent.com:sub": "repo:my-org/shop:environment:production" Remember
- OIDC exchanges a signed per-job token for short-lived cloud credentials
- Requires permissions: id-token: write
- Lock the trust policy to repo and branch or environment via the sub claim
Watch out for
- A trust policy that checks only aud, not sub, trusts every repository on GitHub.