Workload identity: GitLab CI
⚠️
Preview — this feature is being built; the design below is the spec.
GitLab CI issues ID tokens to jobs that ask for them. Each token names the project, branch and pipeline source, so a Syntic trust rule can accept exactly the jobs you intend. This works with GitLab.com and with self-managed GitLab instances whose issuer is reachable over HTTPS.
Claims you can match on
| Claim | Example | Use it to |
|---|---|---|
project_path | acme/billing | Trust one project. |
namespace_path | acme | Combine with other conditions; never alone. |
ref | main | Trust one branch or tag. |
ref_type | branch or tag | Distinguish branches from tags. |
ref_protected | true | Trust only protected branches and tags. |
pipeline_source | push, schedule, merge_request_event | Exclude merge-request pipelines. |
environment | production | Trust deployment jobs for one environment. |
Set it up
Create a service account and trust rule
{
"issuer": "https://gitlab.com",
"audience": "https://app.syntic.ai",
"conditions": {
"project_path": "acme/billing",
"ref": "main",
"ref_protected": "true"
},
"max_token_lifetime_seconds": 900
}For self-managed GitLab, use your instance URL as the issuer, for example https://gitlab.example.com.
Request an ID token in the job and exchange it
summarise:
image: alpine:3.20
id_tokens:
SYNTIC_ID_TOKEN:
aud: https://app.syntic.ai
rules:
- if: $CI_COMMIT_BRANCH == "main"
script:
- apk add --no-cache curl jq python3
- |
export SYNTIC_API_KEY=$(curl -sS https://app.syntic.ai/v1/oauth/token \
-H "syntic-beta: token-exchange-2026-11-01" \
-d grant_type=urn:ietf:params:oauth:grant-type:token-exchange \
-d subject_token_type=urn:ietf:params:oauth:token-type:jwt \
-d service_account=svcacct_01JCB3D5F7H9K1M3P5R7 \
--data-urlencode subject_token="$SYNTIC_ID_TOKEN" | jq -r .access_token)
- python3 scripts/summarise_issues.pyThe id_tokens block makes GitLab place a token with the right audience in SYNTIC_ID_TOKEN. The exchanged token lives only in the job’s environment.
Good rules for GitLab
- Always include
project_path. - Require
ref_protected: "true"for anything with access to production data. - Exclude
merge_request_eventpipelines from service accounts that can do more than read.
Condition values are compared as strings. Write boolean claims as "true" and "false".
Troubleshooting
| Error | Cause |
|---|---|
invalid_grant naming iss | The rule’s issuer does not match your GitLab URL exactly (scheme and host). |
invalid_grant naming aud | The aud in id_tokens differs from the rule’s audience. |
| Variable empty | The job has no id_tokens block. |
Related
Was this page helpful?