Git, CI/CD & GitOpsXLIII · OIDC and Short-Lived CredentialsFoundations
OIDC federation basics — what OIDC is; the trust relationship between CI and cloud
What you'll learn
- Describe what OpenID Connect is — a signed JWT identity layer with claims about the issuer and subject
- Explain the trust relationship between CI forge and cloud: the forge signs tokens, the cloud validates them against a configured provider
- Identify the four claims that every CI-issued OIDC token carries — issuer, audience, subject, and expiration
- Recognise why OIDC federation requires a trust policy on the cloud side and what the policy gates
Prerequisites
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
OpenID Connect is a signed-JWT identity layer. In CI/CD, the forge (GitHub Actions, GitLab CI) issues an OIDC ID token per job. The cloud provider validates the token’s signature against a trusted issuer, checks the token’s claims against a trust policy, and exchanges the validated token for a short-lived STS session. The result: the runner does not hold a credential. The runner holds a request for a credential, signed by the forge, validated by the cloud. The credential’s lifetime is the session’s lifetime; the session’s lifetime is the workload’s lifetime.
What OIDC is
OpenID Connect is an identity layer on top of OAuth 2.0. An OIDC identity provider (the forge) signs a JSON Web Token (JWT) that carries claims about who is asking. A relying party (the cloud) validates the signature against the provider’s published keys and checks the claims against its trust policy.
flowchart LR
F["Forge\n(GitHub Actions)"] -->|signs| T["OIDC ID token\n(JWT, signed)"]
T --> C["Cloud\n(AWS, Azure, GCP)"]
C -->|validates signature| OK["Signature OK?"]
OK -->|yes| P["Trust policy\nchecks claims"]
P -->|match| S["Issues STS session\n(~1 hour)"]
P -->|no-match| D["Denies"]
The four claims every CI-issued OIDC token carries:
iss— issuer. The forge that signed the token. For GitHub Actions,https://token.actions.githubusercontent.com.aud— audience. The service the token is intended for. For AWS,sts.amazonaws.com. For Azure, the application’s client ID.sub— subject. Who is asking. For GitHub Actions, a structured string likerepo:acme/infra:ref:refs/heads/main.exp— expiration. When the token dies. GitHub Actions OIDC tokens are valid for a few minutes; the cloud re-issues a longer STS session on top of the validated token.
The signature is the binding. A token without a valid signature is a token the cloud rejects. The forge’s signing keys are published at a well-known JWKS endpoint; the cloud fetches them; the cloud validates the signature against the published key.
The trust relationship
OIDC federation is two configurations that must agree. The forge side: the workflow declares which audience it wants a token for and which permissions it requests. The cloud side: the trust policy declares which forge, which audience, and which subject patterns may assume the role.
flowchart TB
subgraph FORGE["Forge side"]
W["Workflow declares\nid-token: write"]
W -->|request token| A["Audience:\nsts.amazonaws.com"]
end
subgraph CLOUD["Cloud side"]
TP["Trust policy"]
TP --> I["Issuer:\ntoken.actions.githubusercontent.com"]
TP --> AU["Audience:\nsts.amazonaws.com"]
TP --> S["Subject:\nrepo:acme/infra:ref:refs/heads/main"]
end
FORGE -->|token| CLOUD
CLOUD -->|if claims match| STS["STS session issued"]
If the two configurations disagree, the cloud rejects the token. The common failure modes:
- Audience mismatch. The workflow requests
sts.amazonaws.com; the trust policy expects a different audience; the cloud denies. - Subject too broad. The trust policy uses
sub: repo:acme/*(a wildcard); a workflow in anyacme/*repo can assume the role. The trust is too broad; OIDC is no better than a static key. - Subject too narrow. The trust policy uses
sub: repo:acme/infra:ref:refs/heads/main; a workflow on a feature branch cannot assume the role. The trust is too narrow; legitimate deploys fail.
Why a signed token works
The signing is asymmetric cryptography. The forge holds a private key; the cloud holds the public key (published at the JWKS endpoint). The forge signs the token with the private key; the cloud verifies the signature with the public key. The cloud does not need the forge’s private key; the cloud needs only the public key to verify that the forge signed the token.
- No shared secret. The cloud does not hold a secret shared with the forge. The cloud holds a public key; the public key cannot be used to forge a signature.
- Key rotation. The forge can rotate its signing keys; the cloud re-fetches the JWKS; old tokens remain valid until they expire; new tokens are signed with the new key.
- Compromise containment. A compromise of the cloud’s view of the public key does not compromise the forge’s signing. A compromise of the forge’s signing key compromises all tokens the forge issues — but the forge can rotate the key, and the cloud re-fetches the JWKS.
The trust relationship is asymmetric in the cryptographic sense: the forge has authority; the cloud has verification; the asymmetry is what makes OIDC safe to use across organisational boundaries.
What the cloud sees in the token
The cloud receives the token, validates the signature, parses the claims, and applies the trust policy. The trust policy is a structured match against the claims:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:sub": "repo:acme/infra:ref:refs/heads/main"
}
}
}
]
}
The cloud validates: the issuer is token.actions.githubusercontent.com
(the OIDC provider is registered), the audience is sts.amazonaws.com,
the subject is repo:acme/infra:ref:refs/heads/main. If all three match,
the cloud issues an STS session token with the role’s IAM policy
attached. If any claim fails to match, the cloud denies.
The session token’s lifetime is set by the role’s
MaxSessionDuration (AWS, default 1 hour, maximum 12 hours). The
token dies at expiry. There is no static value to rotate.
Production discipline
- Register the forge as a trusted OIDC provider. Each cloud account that the CI uses has the forge’s OIDC provider registered and its JWKS endpoint trusted.
- Trust policies are as narrow as the legitimate workflows require. The subject claim matches repo + branch + workflow path; the audience matches the cloud service; the issuer matches the forge.
- Trust policies are reviewed when workflow files change. A new workflow file may require a new subject pattern; the policy update is part of the pull request.
- The audience is explicit. The workflow requests the audience; the trust policy validates the audience; the cloud service accepts only the audience it was configured for.
Cross-course references
- Git, CI/CD & GitOps — Part XLI-03 (Production credentials on runners) covers the runner-side blast radius that OIDC eliminates.
- Git, CI/CD & GitOps — Part XLII-06 (The short-lived credential ideal) covers the lifetime principle that OIDC satisfies.
- AWS for Production Sysadmins — Part XXXI (IAM) covers the IAM trust policies that gate OIDC role assumption.
Quiz
Knowledge check · 4 questions
Q1. What is the cryptographic binding that makes an OIDC ID token trustworthy to the cloud?
Q2. A trust policy that allows any repository in the organisation to assume a role via OIDC is the operational equivalent of a static access key with no scope.
Q3. Name the four claims every CI-issued OIDC token carries and explain what each one gates on the cloud side.
Q4. Diagnose why the trust policy fails to gate the OIDC role and prescribe the narrow policy that closes the gap.
Team T configures OIDC federation between GitHub Actions and AWS. The IAM role's trust policy allows `token.actions.githubusercontent.com:sub` to match `repo:acme/*:ref:refs/heads/*`. The role grants AdministratorAccess on the production account. A fork PR from a non-team contributor opens in the `acme/infra` repository; the fork PR's workflow requests an OIDC token; the cloud validates the token; the subject `repo:acme/infra:ref:refs/heads/attacker-branch` matches the wildcard pattern; the cloud issues an AdministratorAccess STS session to the fork PR's runner.
Passing score: 75%. Answers are checked in this browser.