Git, CI/CD & GitOpsXLIII · OIDC and Short-Lived CredentialsTrustPolicy
OIDC trust policy deep dive — sub claims, job_workflow_ref claims, the immutable subject claims change
What you'll learn
- Decode the OIDC token's sub claim and map it to a trust policy subject pattern
- Use the job_workflow_ref claim to gate trust on the specific workflow file path
- Recognise GitHub's immutable subject claim format change and migrate off the legacy pattern
- Write a trust policy that is as narrow as the legitimate workflows require, and no narrower
Prerequisites
Practice
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
The OIDC trust policy is the gate; the token’s claims are the truth.
A precise trust policy is a precise match against the sub and
job_workflow_ref claims — as narrow as the legitimate workflows
require, and no narrower. The sub claim gates repository + branch +
ref; the job_workflow_ref claim gates the specific workflow file
path. GitHub changed the sub claim format in November 2021, and the
legacy pattern is no longer accepted by the issuer; a workflow that
uses the legacy pattern permanently fails.
The sub claim anatomy
The sub claim in a GitHub Actions OIDC token is a colon-separated
string that identifies the workflow run. The current format
(post-November 2021):
repo:ORG/REPO:ref:refs/heads/BRANCH
repo:ORG/REPO:ref:refs/tags/TAG
repo:ORG/REPO:ref:refs/pull/PR_NUMBER/merge
repo:ORG/REPO:environment:ENVIRONMENT_NAME
Each component is a key:value pair:
repo— the repository inORG/REPOformatref— the git ref (branch, tag, or pull request)environment— the GitHub Actions environment (for environment- gated workflows)
For example, the sub claim for a workflow running on the main
branch of the acme/infra repository is
repo:acme/infra:ref:refs/heads/main. The trust policy matches this
string against a pattern:
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:sub": "repo:acme/infra:ref:refs/heads/main"
}
}
A workflow on a different repository, branch, or ref does not match the pattern; the cloud denies.
The job_workflow_ref claim
The job_workflow_ref claim identifies the specific workflow file
that triggered the job. The format:
REPO/.github/workflows/WORKFLOW_FILE@REF
For example, a workflow running from .github/workflows/deploy.yml
on the main branch of the acme/infra repository has the
job_workflow_ref claim
acme/infra/.github/workflows/deploy.yml@refs/heads/main.
The trust policy matches this claim against a pattern to gate the specific workflow file:
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:job_workflow_ref": "acme/infra/.github/workflows/deploy.yml@refs/heads/main"
}
}
A job from a different workflow file does not match the pattern; the
cloud denies. The job_workflow_ref claim is the finer-grained gate
that complements the sub claim.
flowchart LR
SUB["sub claim:\nrepo:acme/infra:ref:refs/heads/main"] --> MATCH["Trust policy match"]
JOB["job_workflow_ref claim:\nacme/infra/.github/workflows/deploy.yml@refs/heads/main"] --> MATCH
MATCH -->|both match| STS["STS session issued"]
MATCH -->|either fails| DENY["Cloud denies"]
The immutable subject claim change
In November 2021, GitHub changed the format of the sub claim to
prevent ambiguity between branches, tags, and environments. The
pre-change format used a flat structure:
repo:ORG/REPO:BRANCH
repo:ORG/REPO:TAG
repo:ORG/REPO:PR_NUMBER
The post-change format uses a structured ref: prefix:
repo:ORG/REPO:ref:refs/heads/BRANCH
repo:ORG/REPO:ref:refs/tags/TAG
repo:ORG/REPO:ref:refs/pull/PR_NUMBER/merge
The change is permanent. Tokens issued after the change use the new format; tokens issued before the change are no longer valid (they expire within minutes anyway). Trust policies written before the change match the legacy format; trust policies written after the change match the new format. A trust policy with the legacy pattern permanently fails against tokens with the new format.
A workflow that was deployed before November 2021 with a trust policy that matches the legacy pattern must be migrated. The migration is a one-line change in the trust policy:
- "token.actions.githubusercontent.com:sub": "repo:acme/infra:main"
+ "token.actions.githubusercontent.com:sub": "repo:acme/infra:ref:refs/heads/main"
The migration is irreversible: the legacy format is no longer issued by GitHub’s OIDC provider. A team that has not migrated has a permanently broken trust policy.
Other useful claims
Beyond sub and job_workflow_ref, the OIDC token carries several
other claims the trust policy can match:
repository— the full repository name (acme/infra)repository_owner— the organisation or user (acme)repository_visibility—public,private, orinternalactor— the user or app that triggered the workflowactor_id— the numeric ID of the actorrun_id— the unique ID of the workflow runrun_number— the sequential number of the workflow runref— the git ref that triggered the workflowref_type—branch,tag, orpull_requestenvironment— the GitHub Actions environment name (for environment-gated workflows)workflow— the workflow file name (for example,deploy.yml)workflow_ref— the full reference to the workflow
A trust policy can match against any of these claims. The combination
of sub, job_workflow_ref, and environment is the typical
fine-grained gate: a specific repository, on a specific branch, from
a specific workflow file, in a specific environment.
Crafting precise policies
A precise trust policy uses StringEquals (not StringLike) with
specific values (not wildcards). The exceptions are cases where the
team needs to support multiple workflows legitimately:
flowchart TB
subgraph STRICT["Strict (preferred)"]
S1["StringEquals on sub\nand job_workflow_ref"]
end
subgraph MEDIUM["Medium (acceptable)"]
M1["StringEquals on sub\nStringLike on job_workflow_ref"]
end
subgraph BROAD["Broad (avoid)"]
B1["StringLike with wildcards\non sub"]
end
STRICT -->|best| OK["OIDC adds value"]
MEDIUM -->|acceptable| OK
BROAD -->|no value| STATIC["OIDC equivalent\nto static key"]
- Strict.
StringEqualson bothsubandjob_workflow_ref; no wildcards. The trust policy matches exactly one workflow file on exactly one branch. The blast radius is the minimum. - Medium.
StringEqualsonsub(specific repo + branch);StringLikeonjob_workflow_refwith a wildcard for multiple workflow files (for example,acme/infra/.github/workflows/deploy-*.yml@refs/heads/main). The blast radius is the set of workflow files the wildcard matches. - Broad.
StringLikewith wildcards onsub(for example,repo:acme/*:ref:refs/heads/*). The blast radius is the union of every workflow in the organisation. OIDC adds no value over a static key; the policy should be tightened.
Production discipline
- Read the token claims before writing the trust policy. The
claims are the truth; the policy is the gate. Decoding the token
(for example, at jwt.io) reveals the actual
subandjob_workflow_refvalues; the policy matches those values. - Use
StringEqualsfor production roles.StringLikewith wildcards is acceptable only for development or staging roles. - Match
subandjob_workflow_reffor the tightest gate. The combination gates repository + branch + workflow file. - Migrate off the legacy
subformat immediately. A trust policy that matchesrepo:ORG/REPO:BRANCHis permanently broken.
Cross-course references
- Git, CI/CD & GitOps — Part XLIII-02 (OIDC federation basics) covers the trust relationship and the four core OIDC claims.
- Git, CI/CD & GitOps — Part XLIII-04 (OIDC in AWS) covers the
AWS trust policy structure with
Conditionblocks. - Git, CI/CD & GitOps — Part XLIII-05 (OIDC in Azure and GCP) covers the federated credential and workload identity pool subject patterns.
Quiz
Knowledge check · 4 questions
Q1. Which OIDC token claim gates the specific workflow file that triggered a job?
Q2. GitHub's OIDC provider still issues tokens with the legacy `sub` format (`repo:ORG/REPO:BRANCH`) for backward compatibility with old trust policies.
Q3. Explain the difference between the legacy and current GitHub Actions OIDC subject claim format, and state the migration required for a trust policy written against the legacy format.
Q4. Diagnose why the OIDC authentication suddenly fails and prescribe the migration.
Team T deployed an OIDC trust policy in 2021 with the legacy `sub` format: `"token.actions.githubusercontent.com:sub": "repo:acme/infra:main"`. The deploys worked for two years. In August 2026, the team starts seeing intermittent authentication failures; eventually every deploy fails. The trust policy has not changed; the workflow has not changed. The IAM role's permission policy has not changed. The only change is a routine workflow run.
Passing score: 75%. Answers are checked in this browser.