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

ClaimExampleUse it to
project_pathacme/billingTrust one project.
namespace_pathacmeCombine with other conditions; never alone.
refmainTrust one branch or tag.
ref_typebranch or tagDistinguish branches from tags.
ref_protectedtrueTrust only protected branches and tags.
pipeline_sourcepush, schedule, merge_request_eventExclude merge-request pipelines.
environmentproductionTrust 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.py

The 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_event pipelines from service accounts that can do more than read.

Condition values are compared as strings. Write boolean claims as "true" and "false".

Troubleshooting

ErrorCause
invalid_grant naming issThe rule’s issuer does not match your GitLab URL exactly (scheme and host).
invalid_grant naming audThe aud in id_tokens differs from the rule’s audience.
Variable emptyThe job has no id_tokens block.
Was this page helpful?