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.
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.
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.
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.
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.
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?
Because it is signed identity evidence. The provider/Vault verifies it and applies a trust policy before issuing a separate scoped credential.
Why should trust rules include stable project/namespace IDs where supported?
Paths and user-facing names can change. Stable IDs reduce accidental authorization drift and make the intended workload identity more durable.
A token expires in five minutes. Is a trust rule that allows every branch safe?
No. Short lifetime limits replay but does not fix over-broad authorization. Ref/environment/project conditions must still be narrow.
What should you log when debugging identity?
Safe metadata such as pipeline/job/source IDs, audience name, selected non-sensitive claim values, provider role name, HTTP/status code, request ID and credential expiry metadata—not the raw JWT or returned secret.
How is CI_JOB_TOKEN different from an OIDC ID token?
CI_JOB_TOKEN is primarily for GitLab-scoped authorization. An ID token is a signed OIDC assertion intended for an external relying party to validate and exchange.
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.
- OIDC authentication using ID tokens — official reference.
- Connect to cloud services — official reference.
- CI/CD YAML — id_tokens — official reference.
- AWS OIDC tutorial — official reference.
- Azure OIDC tutorial — official reference.
- Google Cloud workload identity federation — official reference.
- Use HashiCorp Vault secrets — official reference.
- Vault authentication tutorial — official reference.
- External secrets in CI/CD — official reference.
- GitLab deprecations and removals — official reference.
- OpenID Connect Core 1.0 — official reference.
- RFC 7519 — JSON Web Token — official reference.
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.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.