GitLab CI lets a job declare id_tokens, each a short lived signed token with its own audience. A cloud provider that trusts GitLab can check the token's claims and issue temporary credentials, so you store no long term key. Your trust policy should rely on stable IDs and protected refs.
How the keyword works
In .gitlab-ci.yml you declare one or more named tokens under id_tokens in a job, each with its own settings. The tokens are then available as variables in script, before_script and after_script. GitLab's example defines two tokens with different audiences and passes each to a separate authentication script.
The audience is set per token. If you omit it, it defaults to the GitLab instance domain. Use a distinct one for every service you authenticate to.
The claims that matter
| Claim | What it tells the cloud |
|---|---|
sub | Default format is the project path with ref type and ref, for example project_path:my-group/my-project:ref_type:branch:ref:feature-branch-1 |
ref_protected | Always present. True if the Git ref is protected, false otherwise |
project_id, namespace_id | Stable numeric identifiers |
aud | The audience you configured |
GitLab documents that since version 18.7 the subject can also include ref_protected, plus environment_protected and deployment_tier when a job specifies an environment.
Writing a trust policy that survives renames
Project paths change when a project is renamed or moved. GitLab's guidance is to include stable identifiers alongside sub, noting that project_id is globally unique and remains the same for the life of the project. It also warns that if a project_path was previously used by another project, token issuance may be blocked until you switch to an ID based subject such as project_id:ID:ref_type:TYPE:ref:REF.
It cautions against using user_login or user_email as the sole condition, because users can change them.
Use ref_protected as a condition so only protected branches or tags can assume a deploy role. This matters because anyone who can push a branch can run a pipeline on it, but only maintainers control which branches are protected.
Provider differences to expect
GitLab says that on GitLab.com, AWS supports several additional condition keys for the gitlab.com provider, while on GitLab Self-Managed and GitLab Dedicated only sub is supported. If you self host, plan your conditions around sub and encode what you need into its format.
Token lifetime
Tokens are signed with RS256 using a dedicated private key. The expiry is set to the job's timeout if one is specified, or 5 minutes if not. That is short, but it still means a token printed in a job log is usable until it expires, so never echo it.
A setup you can copy in outline
- Declare an
id_tokensentry with the audience your cloud expects. - Create a role or federated identity in the cloud that trusts GitLab as an identity provider.
- In the trust condition, match
aud, matchproject_id, and requireref_protectedto be true for deploys. - Use a second token with a different audience if the same job also talks to another service.
- Test on a protected branch and on an unprotected one, and confirm the second fails.
Quick check
How long does a GitLab id_token last?
GitLab sets expiry to the job's timeout if specified, or 5 minutes if no timeout is specified.
Why prefer project_id in a trust policy?
It is globally unique and unchanged for the life of the project, so renames and moves do not break or hijack the policy.
Safeguard does not manage your cloud accounts. It scans your code and dependencies, applies pull request policies and gates, and automates compliance evidence. Free scan: gold.safeguard.sh/scan.
Sources
Last reviewed: 19 October 2026.