OpenID Connect, Cloud Federation, Trust Policies, and Secretless Deployment: Core Concepts and Mental Model
Chapter 19 governed who may enter a deployment environment. Chapter 20 removes the assumption that the approved job must hold a long-lived cloud key. Instead, the job proves its workload identity to GitHub, the provider validates narrow claims, and temporary authority exists only long enough to perform the authorized operation.
Learning objectives
- Explain the difference between GitHub's OIDC identity token and a cloud access credential.
- Trace issuer, audience, subject, repository/workflow/ref/environment claims through a trust decision.
-
Explain exactly what
id-token: writegrants—and what it does not grant. - Design narrow trust policies around the identity that is actually expected to deploy.
- Preserve run, revision, runner, claim-policy, credential-lifetime, and provider-audit evidence.
1. The practical problem: long-lived keys outlive the run that needed them
Chapter 19 separated a deployment target from the branch that produced it. The next problem is credential lifetime. A repository secret containing a cloud access key can remain valid for weeks or months, while the deployment job that needed it might run for only a few minutes. If the secret is copied, exposed in another system, or simply forgotten during rotation, the credential survives the workflow run and can be used outside the evidence chain that authorized the deployment.
OpenID Connect changes the model. Instead of storing a cloud password in GitHub, an eligible job asks GitHub for a short-lived identity assertion. The cloud provider verifies that assertion against a preconfigured trust policy and, only if the claims match, returns a temporary provider credential. This separates identity from authorization: GitHub proves what workload is asking; the provider decides what that workload may receive.
2. Mental model: identity assertion → provider authorization → temporary credential
Read the chain in order. An event selects a workflow revision and
creates a run. A deployment job reaches the point where it needs
cloud identity. The job must have id-token: write,
which permits it to request a JWT from GitHub's OIDC issuer. The JWT
carries an issuer, audience, subject, timestamps, and
GitHub-specific claims. The provider validates signature, issuer,
audience, time bounds, and its own trust conditions. Only then does
the provider issue a short-lived credential for a narrowly scoped
role or service account. That credential is used for the deployment
action and then expires.
flowchart TD A[Event + exact source SHA] --> B[Workflow job] B --> C[id-token: write] C --> D[GitHub OIDC JWT] D --> E[Provider validates issuer audience subject and claims] E -->|match| F[Short-lived provider credential] E -->|mismatch| G[Denied federation request] F --> H[Scoped deployment/API call] H --> I[Provider audit record + target evidence] F --> J[Credential expires]
No arrow should be skipped. id-token: write does not
jump directly to cloud permissions. A valid GitHub-signed JWT does
not mean the provider must accept it. A successful token exchange
does not prove the deployment changed the intended target. Each
transition has separate evidence.
3. Keep the state layers separate
| Layer | State to record | What it proves |
|---|---|---|
| Event / revision | event, ref, GITHUB_SHA, run ID, attempt |
Which source and workflow run requested identity |
| Workflow configuration | job-level permissions, environment, reusable-workflow reference | What GitHub configuration was evaluated |
| GitHub identity | issuer, audience, subject, repository/workflow/ref/environment claims | What workload GitHub asserted |
| Provider trust | role/service-account identity and exact claim conditions | Which GitHub identities the provider is willing to trust |
| Provider credential | temporary session identity and expiry—not the secret value | Which temporary authority was issued |
| Deployment | target identity, immutable artifact/digest, provider audit/deployment record | What authorized side effect actually occurred |
A useful incident record can therefore say: “run 412, attempt 1, source SHA X, production environment, expected subject Y, AWS role Z, temporary session issued at time T, deployment changed target Q.” It should never need the raw OIDC JWT or temporary credential value.
4. id-token: write is token-minting permission, not
cloud write access
GitHub requires id-token: write at workflow or job
scope before a job can request an OIDC token. The word
write is easy to misread: it does
not grant write access to repository contents,
GitHub APIs, AWS, Azure, or Google Cloud. It only enables the OIDC
token request mechanism for that job.
jobs:
deploy:
runs-on: ubuntu-24.04
environment: production
permissions:
contents: read
id-token: write
steps:
- run: echo "Cloud authorization still depends on provider trust."
Prefer job-level permission when only the deployment job needs federation. A build or lint job normally has no reason to mint an identity token. Narrowing the permission makes the trust boundary visible during review.
5. The three claims every federation design starts with
The issuer (iss) identifies GitHub's OIDC issuer:
https://token.actions.githubusercontent.com. The
audience (aud) identifies the intended token consumer
and is provider-specific. The subject (sub) is the main
workload identity string used by many trust policies. GitHub also
exposes useful claims such as repository_id,
repository_owner_id, workflow_ref,
workflow_sha, job_workflow_ref,
job_workflow_sha, ref,
environment, run_id,
run_attempt, and runner_environment.
Trust the smallest stable identity that matches the intended deployment. A provider policy that accepts “any repository in my organization” is broader than a policy that accepts one repository, one environment, and an expected reusable deployment workflow. When provider capabilities allow it, immutable repository and owner IDs are stronger anchors than names that can be renamed or reused.
sub format containing owner and repository IDs.
Repositories created earlier retain the previous name-based format
unless they opt in; renames/transfers can also move a repository to
the immutable format. Do not paste a trust-policy subject from an old
tutorial. Inspect the repository's actual OIDC configuration and
design the provider trust condition to match it.
6. Branch, pull-request, and environment subjects are different identities
Without an environment, a branch-oriented subject identifies the repository plus the triggering ref. A pull-request run has a pull-request subject form. When the job references an environment, the environment becomes the subject context. That is why Chapter 19 matters: a production environment can become part of the workload identity rather than relying only on a mutable branch name.
Name-based legacy examples:
repo:acme-labs/widget-api:ref:refs/heads/main
repo:acme-labs/widget-api:pull_request
repo:acme-labs/widget-api:environment:production
Synthetic immutable-format example used in this chapter:
repo:acme-labs@100200300/widget-api@400500600:environment:production
Those are identity examples, not universal copy-paste values. The learner's repository might use another format or customized subject configuration. The operating rule is to verify the actual claim first, then bind the provider to that claim.
7. Reusable deployment workflows add another useful identity
Chapter 16 treated reusable workflows as versioned contracts. OIDC
exposes job_workflow_ref and
job_workflow_sha for jobs that execute through a
reusable workflow. That lets a provider require not only “this
repository and environment,” but also “this approved central
deployment workflow.” It is a powerful way to make a golden
deployment path enforceable outside GitHub.
The called workflow still cannot elevate token permissions beyond the caller chain. If the caller does not make OIDC available as required by the current same-organization/cross-organization rules, the called workflow cannot manufacture that permission. The cloud trust policy remains a second independent authorization boundary.
8. Short-lived does not mean unbounded
The GitHub-issued OIDC JWT itself is intentionally short-lived. The provider then controls the lifetime and privileges of the credential it returns. AWS STS sessions, Microsoft Entra access tokens, and Google Cloud federated/impersonated credentials have different lifetime controls. A design should record the configured lifetime and request only what the deployment needs.
Expiration limits blast radius, but it does not remove the need for least privilege. A five-minute administrator credential can still do enormous damage. Federation improves secret lifecycle; authorization policy still determines impact.
9. Read-only inspection before any provider change
# Local source identity
git rev-parse HEAD
git status --short
# Read the workflow permissions and environment before dispatch
grep -n -E 'permissions:|id-token:|environment:|uses:' \
.github/workflows/oidc-production.yml
# Runtime evidence that is safe to print
printf 'run_id=%s\nattempt=%s\nsha=%s\nref=%s\n' \
"$GITHUB_RUN_ID" "$GITHUB_RUN_ATTEMPT" "$GITHUB_SHA" "$GITHUB_REF"
Do not print ACTIONS_ID_TOKEN_REQUEST_TOKEN, a raw JWT,
cloud access tokens, or provider credential files. If claim
inspection is required in production, prefer a provider/tooling
feature that reports non-secret claim fields, or decode only an
explicitly synthetic token in a local lab.
10. Why this matters in DevOps
Federation aligns credential lifetime with automation lifetime. It also gives the cloud provider a verifiable workload identity that can be correlated with a GitHub run, exact revision, environment, and reusable workflow. That improves rotation burden, revocation response, auditability, and separation between build and deployment jobs.
The production invariant is not “OIDC is enabled.” It is: the exact authorized workload can mint a GitHub identity assertion, the provider accepts only the intended claim set, the resulting credential is short-lived and least-privileged, the exact verified artifact is deployed, and both GitHub and provider evidence point to the same operation.
11. Lesson summary
-
id-token: writeallows a job to request an OIDC JWT; it does not grant cloud authorization. - The provider verifies issuer, audience, subject/claims, and its own trust policy before issuing temporary credentials.
- Repository, workflow, ref, environment, and immutable IDs are separate identity dimensions.
- GitHub's 2026 immutable default subject format means old name-only tutorials cannot be treated as universal.
- Short lifetime reduces credential exposure but does not replace least-privilege provider permissions.
Knowledge check
Why is id-token: write not equivalent to cloud
write access?
It only lets the job request a GitHub OIDC identity token. The cloud provider independently evaluates its trust policy and then decides whether and what temporary credential to issue.
A repository was created in August 2026. Should you assume its
subject is
repo:ORG/REPO:environment:production?
No. New repositories created after July 15, 2026 use GitHub's immutable default subject format with owner and repository IDs. Inspect the actual configured subject before writing provider trust.
What evidence should be retained instead of a raw JWT?
Run ID/attempt, exact SHA/ref, expected non-secret claim values, provider role/service-account identity, policy revision, temporary credential lifetime, and provider/deployment audit records.
Why can an environment strengthen OIDC trust?
An environment can become part of the workload subject/context and can add GitHub-side deployment protection before the federated credential is requested.
A provider accepts a GitHub JWT but deployment still fails. Which boundary should you inspect next?
Inspect the provider-issued credential's role/permissions and target API/deployment state. Token acceptance proves identity federation, not that the temporary principal is authorized for every resource action.
Official references and version notes
Version-sensitive GitHub Actions and provider behavior in this lesson was rechecked on 2026-09-10. Re-verify current OIDC subject format, provider trust syntax, action release SHAs, plan/visibility constraints, and temporary-credential lifetimes before production use.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.