Chapter 27Lesson 01~185 minutes

ID Tokens, OIDC Workload Identity, Cloud Federation, Vault Authentication, and Secretless Deployments: Concepts, Architecture, and Mental Model

Replace stored cloud secrets with a verifiable workload-identity chain: GitLab job → explicit ID token → provider trust evaluation → short-lived credential → bounded action.

OIDCID tokensWorkload identityLeast privilegeTrust policy

Learning objectives

  • Explain the difference between a GitLab ID token, a provider trust policy, and the temporary credential returned after federation.
  • Trace authorized GitLab job → id_tokens → signed OIDC claims → external trust decision → bounded short-lived credential → external action.
  • Identify which claims are useful for project/ref/environment scoping and why audience matching is mandatory.
  • Distinguish GitLab job identity from CI_JOB_TOKEN, PATs, deploy tokens, static cloud keys, and user interactive OIDC.
  • Inspect identity state without printing a live JWT or sensitive exchanged credential.

1. The practical problem: long-lived cloud keys turn one leaked variable into durable access

Chapter 26 built an evidence chain around exact source and artifact identity. Deployment identity needs the same discipline. A conventional pipeline might store an AWS access key, Azure client secret, GCP service-account key, or Vault token as a long-lived CI/CD variable. That works, but the credential can outlive the job, be copied to the wrong project, or remain valid after the source revision that needed it is gone.

OIDC workload identity changes the question. Instead of asking GitLab to store a provider credential, the job asks GitLab for a short-lived, signed statement about the job. The external provider validates that statement and decides whether this particular job is allowed to assume a narrowly scoped role. The provider then returns a temporary credential.

Core rule: an ID token is not a cloud credential and not a permission grant. It is signed identity evidence. The provider/Vault trust policy performs authorization and returns a separate short-lived credential only when the claims satisfy policy.

2. Mental model: identity evidence crosses a trust boundary before authorization exists

The flow has six distinct states. GitLab compiles a job that explicitly requests an ID token. GitLab signs a JWT whose audience and claims describe that job. The job sends the token to exactly the intended relying party. The provider verifies signature/issuer/audience/time and evaluates its trust conditions. Only then may it issue a short-lived credential, which is used for a bounded API or deployment action.

flowchart TD A[Authorized GitLab job] --> B[id_tokens request] B --> C[Signed ID token: iss + aud + sub + claims] C --> D[Provider/Vault verification + trust policy] D -->|allow| E[Short-lived scoped credential] D -->|deny| X[Authorization denial evidence] E --> F[Bounded API/deployment action] F --> G[External read-back + audit evidence]

The denial path is first-class evidence. A secure design should be able to prove not only that main can assume a deployment role, but that an attacker-like feature branch, fork, unprotected ref, wrong environment, or wrong audience cannot.

3. Keep the identity layers separate

State layer Evidence to capture Why it matters
Source/revision CI_PIPELINE_SOURCE, ref/ref type, CI_COMMIT_SHA, pipeline/job IDs Workload identity must be tied to the exact job and source hypothesis being authorized.
Compiled CI configuration Merged YAML, id_tokens request, rules result, environment declaration, CI config SHA Proves which token request and deployment job GitLab actually compiled.
Job/runner Job ID, runner ID/environment, executor/image/tool versions The token belongs to a job execution context, not to a repository in the abstract.
OIDC token request Token variable name, audience, intended relying party The audience is an authorization boundary; do not mint one broad token for unrelated services by default.
Token claims Issuer, subject, project/job IDs, ref, ref_protected, environment, SHA, expiry These are provider inputs. Record safe selected claims, never the raw live JWT.
Provider/Vault trust Issuer/JWKS, audience, bound subject/IDs/ref/environment conditions, role/policy version The external service—not GitLab—decides whether the presented identity is trusted.
Short-lived credential Credential class, role, scope, issue/expiry metadata; never secret material Federation reduces stored-secret lifetime only if the exchanged credential is itself narrow.
External action Exact API/resource/namespace changed, request/result ID, read-back verification Authentication success is not deployment success or target health.
Governance Trust-policy review, owner, emergency change evidence, expiration/rotation expectations Provider-side trust is production authorization code and must be reviewable.

4. Terms before YAML

Term Meaning Common mistake
OIDC provider / issuer The GitLab instance that issues signed ID tokens and publishes discovery/signing metadata. Treating any JWT-shaped value as trustworthy without validating issuer/signature.
ID token A short-lived JWT explicitly requested with id_tokens. Using it as a general bearer token against unrelated APIs.
Audience (aud) The intended relying party/service for the token. Using one broad audience for unrelated providers or ignoring audience validation.
Subject (sub) Default project/ref-oriented subject string; project administrators can customize it through the API. Authorizing every branch by matching a project prefix only.
Bound claims / trust conditions Provider/Vault checks on project/ref/environment/stable IDs and similar claims. Checking only a mutable username/email or only the issuer.
Federation / token exchange Provider validates the GitLab token and returns temporary provider credentials. Assuming the GitLab token itself is an AWS/Azure/GCP credential.
Short-lived credential Temporary provider/Vault credential scoped by the assumed role/policy. Giving the temporary role administrator privileges because it expires.

5. Current GitLab claims: choose stable and contextual signals

Current GitLab ID tokens include standard JWT claims such as iss, sub, aud, exp, nbf, iat, and jti. GitLab also provides contextual claims including project_id, project_path, namespace identity, job_project_id, pipeline_id, pipeline_source, job_id, ref, ref_type, ref_path, ref_protected, runner identity, sha, and CI configuration identity. When a job declares an environment, environment name/protection/tier/action claims are present.

For provider trust, prefer stable identifiers such as project/namespace IDs alongside readable path/ref conditions when supported. A path can be renamed; an email or username can change. Stable IDs reduce accidental privilege continuity across renames or identity changes.

Merge-request nuance: current tokens expose both source-project-oriented project claims and job-project-oriented claims. When cross-project or fork pipelines matter, authorize the project that actually runs the job, not the project you merely expected.

6. Expiration limits replay; it does not replace scope

GitLab sets token expiration to the job timeout when one is specified, or five minutes if the job has no timeout. Short lifetime reduces replay opportunity, but it does not make a wildcard trust policy safe. A five-minute token that can assume an administrator role from every branch is still a serious authorization flaw.

Provider credentials have their own lifetime and scope. Record the role and expiry metadata, but never copy the raw credential into an artifact or log.

7. Read-only inspection before requesting or exchanging identity

Inspect the candidate source and declared job controls before any external exchange:

git status --short
git rev-parse HEAD
git show -s --format='%H %cI %s' HEAD
# In CI, record only safe metadata:
printf 'pipeline_source=%s\n' "$CI_PIPELINE_SOURCE"
printf 'sha=%s\n' "$CI_COMMIT_SHA"
printf 'project=%s\n' "$CI_PROJECT_PATH"
printf 'ref=%s\n' "$CI_COMMIT_REF_NAME"
printf 'protected=%s\n' "$CI_COMMIT_REF_PROTECTED"
printf 'job_id=%s pipeline_id=%s\n' "$CI_JOB_ID" "$CI_PIPELINE_ID"

Then inspect the merged/compiled CI configuration and provider trust policy. The question is not “does a token exist?” but “which job can request which audience, and what exact provider policy will accept it?”

8. Explicit token request: one relying party, one audience

deploy_preview:
  stage: deploy
  id_tokens:
    CLOUD_ID_TOKEN:
      aud: https://sts.example.test
  environment:
    name: staging
    deployment_tier: staging
  script:
    - ./exchange-and-deploy.sh "$CLOUD_ID_TOKEN"

id_tokens changes the identity state of the job: GitLab makes the named token available to that job. It does not configure the external provider and does not grant provider permissions. The relying party still has to validate the GitLab issuer/signature, exact audience, token time window, and trust conditions.

Do not debug by echoing the token. Treat a live JWT like a credential-bearing assertion. If you must validate behavior, prefer provider-side denial details or a trusted verifier that emits only selected non-sensitive claim names/values and never persists the raw token.

9. ID token versus other GitLab identities

Identity Primary purpose External trust boundary
CI_JOB_TOKEN GitLab API/package/repository access with job-scoped permissions. GitLab authorization model; not a generic cloud federation identity.
ID token / OIDC Prove job identity to an external OIDC-aware relying party. External provider/Vault validates token and maps claims to a role.
PAT User-associated GitLab API access. Longer-lived human credential; avoid for workload federation when job identity works.
Deploy token Project/group package/registry/repository deployment access. Static credential with explicit scopes; useful where OIDC is unsupported.
Static cloud key Provider-native long-lived credential. Provider trust exists before the job; highest rotation/leak burden.

10. Vault is a broker option, not magic secretlessness

Vault can validate a GitLab ID token and issue a Vault token or secret response according to a bound role. GitLab's native secrets:vault integration is currently Premium/Ultimate. The underlying architecture is still useful on Free: a job can exchange an ID token manually with any OIDC/JWT-aware provider you operate, but the mandatory course lab uses a local mock to avoid requiring a Vault server.

Starting with Vault 1.17, roles receiving JWTs with an aud claim require bound audiences. This is the right mental model: audience validation belongs in the relying party.

11. Cloud providers differ; the trust invariant does not

Provider path GitLab identity side Provider-side responsibility
AWS STS Explicit GitLab ID token; GitLab.com supports several GitLab claims as AWS condition keys. OIDC provider + IAM role trust conditions + least-privilege role permissions.
Azure / Entra Explicit GitLab ID token used as federated token. Federated identity credential matches issuer/subject/audience; service principal permissions stay narrow.
Google Cloud WIF Explicit GitLab ID token exchanged through workload identity federation. Pool/provider attribute mapping + conditions + service-account/resource IAM.
Vault JWT auth Explicit GitLab ID token. JWT auth role validates issuer/audience/bound claims and maps to Vault policy.

On self-managed GitLab, providers may need public access to OIDC discovery/JWKS metadata. That is an infrastructure requirement, not a reason to weaken TLS or copy signing keys into jobs.

Knowledge check

Why does an ID token not authorize a deployment by itself?

Why should trust rules include stable project/namespace IDs where supported?

A token expires in five minutes. Is a trust rule that allows every branch safe?

What should you log when debugging identity?

How is CI_JOB_TOKEN different from an OIDC ID token?

Next lesson

Bridge to the hands-on workflow

Next you will model this trust chain locally. No real cloud account, Vault instance, private key, or live ID token is required: synthetic claim sets will make the authorization logic observable without risking credentials.

Version and compatibility note

GitLab and GitLab Runner evolve continuously. Treat version-sensitive YAML, runner/executor behavior, APIs, security features, policy controls, tiers, and deprecations as assumptions to verify against the current official GitLab documentation before production use. Preserve the exact project/ref/SHA, compiled configuration, pipeline/job IDs, runner/tool versions, and external target evidence used for any reproducible lab or incident record.

Official references and version notes

Documentation verification date: 2026-09-12. GitLab documents explicit id_tokens as available on Free, Premium, and Ultimate across GitLab.com, Self-Managed, and Dedicated. CI_JOB_JWT/CI_JOB_JWT_V2 were removed in GitLab 17.0; use explicit ID tokens instead. ID tokens are RS256-signed. Their expiry is the job timeout when one is specified, otherwise five minutes. Current custom claims include project/namespace identity, job identity, ref/ref protection, pipeline source, runner identity, source SHA, CI configuration identity, and—when the job declares an environment—environment name/protection/tier/action. GitLab recommends stable IDs such as project/namespace IDs in trust policy conditions where the target provider supports them. Native secrets:vault integration is Premium/Ultimate, while ID-token federation itself is Free. The examples intentionally do not decode a live token in logs. Although GitLab troubleshooting documentation shows token decoding for diagnosis, this course keeps the mandatory path safer by using synthetic claims or trusted provider-side diagnostics instead.

Current assumptions used in this chapter: Version-sensitive YAML, Runner/executor behavior, APIs, security features, policy controls, tiers, and deprecations must be verified against the exact GitLab, GitLab Runner, tool, and external-system versions used in production. Preserve the exact project/ref/SHA, compiled configuration, pipeline/job IDs, runner/tool versions, and external target evidence used for reproducible work.

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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.