Skip to main content
RunBook Academy

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

Advanced⏱ ~28 min🧪 Lab requiredgit

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

Not yet marked complete on this device.

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 in ORG/REPO format
  • ref — 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_visibilitypublic, private, or internal
  • actor — the user or app that triggered the workflow
  • actor_id — the numeric ID of the actor
  • run_id — the unique ID of the workflow run
  • run_number — the sequential number of the workflow run
  • ref — the git ref that triggered the workflow
  • ref_typebranch, tag, or pull_request
  • environment — 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. StringEquals on both sub and job_workflow_ref; no wildcards. The trust policy matches exactly one workflow file on exactly one branch. The blast radius is the minimum.
  • Medium. StringEquals on sub (specific repo + branch); StringLike on job_workflow_ref with 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. StringLike with wildcards on sub (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

  1. 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 sub and job_workflow_ref values; the policy matches those values.
  2. Use StringEquals for production roles. StringLike with wildcards is acceptable only for development or staging roles.
  3. Match sub and job_workflow_ref for the tightest gate. The combination gates repository + branch + workflow file.
  4. Migrate off the legacy sub format immediately. A trust policy that matches repo:ORG/REPO:BRANCH is 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 Condition blocks.
  • 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

  1. Q1. Which OIDC token claim gates the specific workflow file that triggered a job?

  2. Q2. GitHub's OIDC provider still issues tokens with the legacy `sub` format (`repo:ORG/REPO:BRANCH`) for backward compatibility with old trust policies.

  3. 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.

  4. 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.