skip to content

Why authenticate GitHub Actions to a cloud provider with OIDC instead of a stored long-lived key?

level: seniorimportance: should knowfreq 47%

answer

  1. Replaces possession with proof of identity
  2. A token is minted per run, not stored
  3. One job permission has to be requested explicitly
  4. One claim encodes repository and ref or environment
  5. The trust condition on the cloud side can be too loose

basics

~20 s

With OIDC the job requests a short-lived signed token from GitHub and exchanges it for temporary cloud credentials. Nothing long-lived is stored in GitHub, so there is no key to leak, rotate, or exfiltrate from a workflow.

solid answer

~50 s

Storing a cloud access key as a repository secret creates a permanent credential that lives in GitHub, must be rotated, and is worth stealing forever. OIDC removes it. The job requests a signed JSON Web Token from GitHub's OIDC provider — which requires the job to hold the `id-token` write permission — and presents it to the cloud provider. The provider has been configured to trust GitHub's issuer and to validate the token's claims, most importantly `sub`, which encodes the repository and the ref or environment the job is running for, and `aud`. If the claims match the configured trust condition, the provider returns temporary credentials for that run. The security gain is twofold: credentials are minted per run and expire quickly, and access is scoped by identity rather than by possession of a string. The failure mode to avoid is a trust condition matching too broadly — any repository in the organisation, or any ref — which turns a per-branch grant into an organisation-wide one.

code

json · 9 lines
json
{
  "iss": "https://token.actions.githubusercontent.com",
  "sub": "repo:my-org/my-repo:ref:refs/heads/main",
  "aud": "my-cloud-audience",
  "repository": "my-org/my-repo",
  "repository_owner": "my-org",
  "ref": "refs/heads/main",
  "workflow": "deploy"
}

go deeper

for a junior

Know that GitHub Actions can authenticate to a cloud provider without storing a permanent key, by exchanging a short-lived token GitHub issues for the run.

for a middle

Explain the exchange: the job requests a signed token, the provider verifies it against GitHub's issuer and claims, and returns temporary credentials. Know that the id-token permission must be requested.

for a senior

Show you have configured it: which claims the trust condition pins, why the subject encodes repository and ref or environment, and how an over-broad condition undoes the whole benefit.

for a principal

Own the migration story — retiring stored cloud keys across many repositories, standardising trust conditions, and separating production from preview identities so a workflow change cannot escalate access.

## The problem with a stored key The traditional pattern is to create a credential in the cloud provider, paste it into a repository or organisation secret, and have the workflow use it. Three things are wrong with that. It is **long-lived**, so it stays valuable to an attacker indefinitely. It is **bearer-based**, so anyone who obtains the string is indistinguishable from the legitimate workflow. And it is **static**, so it must be rotated on a schedule that nobody enjoys owning, and the rotation itself tends to break builds. A repository secret is encrypted at rest and redacted from logs, and those controls are real — but they protect the storage, not the consequence. A workflow step is code, and any step in the job can read the secret it is given. ## How OIDC changes the shape OpenID Connect federation replaces possession with proof of identity. GitHub runs an OIDC provider that can issue a signed JSON Web Token describing the job that asked for it. The job requests that token — which requires the workflow to grant the job the `id-token` write permission, since it is not available by default — and presents it to the cloud provider in exchange for temporary credentials. On the cloud side, an administrator configures federation once: trust tokens issued by GitHub's OIDC issuer, and accept them only when specified claims match. The token is signed by GitHub and verified against GitHub's published keys, so the cloud provider is not trusting a string a human copied; it is verifying an assertion about which repository, on which ref, is running right now. ## The claims that matter The pivotal claim is `sub`, the subject, which GitHub composes from the identity of the run. Its shapes include a repository plus a ref, and a repository plus a deployment environment. That structure is what lets you write a trust condition meaning "only the main branch of this one repository" or "only jobs running against the production environment of this one repository". The `aud` claim identifies the intended audience and should also be constrained, so a token minted for one relying party cannot be replayed at another. ## Where teams get it wrong - **Over-broad subject matching.** A condition that accepts any repository in the organisation, or any ref within a repository, means any workflow anyone can run — including one added on a throwaway branch — obtains production credentials. This is the single most common misconfiguration and it silently converts a good mechanism into a worse-than-before one. - **Ignoring `aud`.** Accepting any audience widens what a token can be presented to. - **Forgetting pull-request refs.** A subject condition written loosely can match jobs running for a pull request, which is exactly the context where less-trusted code executes. - **Assuming OIDC removes the need for least privilege.** The credentials the provider returns still carry whatever authority was configured. Short-lived and over-powered is still over-powered. ## What you still have to do OIDC eliminates the stored secret; it does not eliminate governance. You still scope the returned permissions narrowly, still separate the identity used for a production deployment from the one used for a preview, and still review changes to workflow files carefully — because the workflow is what asserts which environment it is running for, and the trust condition is only as meaningful as the environment protections behind it. ## Answering it in an interview State the mechanism in one breath — job requests a signed token from GitHub, presents it to the provider, receives short-lived credentials — then the two benefits: nothing long-lived stored, and access bound to a verifiable identity rather than a copied string. Then, unprompted, name the failure mode: an over-broad subject condition. Interviewers ask this question specifically to hear whether you know that OIDC is only as strong as the trust condition on the other side, and volunteering that is what separates a memorised answer from operational experience.

  • Which claim should a cloud trust condition pin, and why?
    The `sub` claim, because it encodes the repository together with the ref or deployment environment the job is running for. Pinning it is what turns "some GitHub workflow" into "the main branch of this one repository". Constrain `aud` as well so a token cannot be replayed at a different relying party.
  • What is the most common way an OIDC setup ends up less safe than a stored key?
    Writing the trust condition too broadly — matching any repository in the organisation, or any ref. Then any workflow anyone can push, including one on a throwaway branch or a pull request, obtains production credentials. The mechanism is sound; the grant is not.
  • Does OIDC mean you no longer need least privilege on the cloud side?
    No. Federation controls who can obtain credentials, not what those credentials may do. The temporary credentials carry whatever authority was configured, so scope them narrowly and separate the identity used for production from the one used for previews. Short-lived and over-powered is still over-powered.

saying these in an interview costs you the question

  • Thinks OIDC just stores the cloud key more securely
  • Grants the trust condition to any repository in the organisation
  • Believes the token itself grants cloud access directly
  • Assumes the id-token permission is available by default
  • Says short-lived credentials make least privilege unnecessary

context