GitLab CI_JOB_TOKEN: Cross-Project Allowlisting and Permission Boundaries
Scope GitLab CI_JOB_TOKEN access across projects with inbound allowlists, trigger-user permissions, fine-grained API rules, and reproducible denial tests.
GitLab’s CI_JOB_TOKEN is a short-lived credential created for an individual CI job. Cross-project access is governed by more than whether the token exists: the target project controls which source projects or groups are allowlisted, the user who triggered the pipeline must already have the needed target-project permissions, and the requested endpoint must accept a job token. Treat those as three separate gates when building a release or artifact handoff.
This separation explains many “the job can clone one project but gets 403 on another” incidents. A job token is narrower than a personal access token, but it is still a bearer credential available to code running in the job. Allowlisting is an inbound boundary on the target project. It does not make untrusted CI code safe or grant new repository membership to the source project’s users.
Trace source, target, and triggering user
When a pipeline job starts, GitLab provides a unique CI_JOB_TOKEN that is valid while the job runs. GitLab documents that its access level is derived from the user who triggered the pipeline, while the token can access fewer resources than a personal access token. A push pipeline, manual job, and scheduled pipeline can therefore have different effective authorization even when the YAML and token variable look identical.
For access from project A to project B, configure project B (the target) to allowlist project A or a group containing A. By default, the target project’s allowlist contains itself. Adding a source project does not grant its members access to the target: the triggering user must separately have the target role or permission needed for the operation.
Be deliberate about choosing a project or group entry. A project entry keeps the trust relationship narrow. A group entry includes its projects and nested subgroups, including projects created there later. Review group membership and subgroup creation as part of the credential boundary rather than treating the group path as a static list of current repositories.
A narrowly scoped artifact download
For an artifact handoff, prefer the dedicated artifacts endpoint and the job-token header instead of embedding a token in a repository URL or creating a long-lived personal access token. This example runs in source project A and downloads a named artifact from a job in target project B:
variables:
TARGET_PROJECT_ID: "12345" # Reviewed numeric ID for project B.
TARGET_REF: "main" # Protected/reviewed ref, not untrusted input.
download-approved-artifact:
script:
- >-
curl --fail --location --silent --show-error
--header "JOB-TOKEN: ${CI_JOB_TOKEN}"
"${CI_API_V4_URL}/projects/${TARGET_PROJECT_ID}/jobs/artifacts/${TARGET_REF}/download?job=build"
--output artifact.zip
Use a reviewed project identifier and a ref that the release policy permits; if a ref is supplied dynamically, URL-encode it and validate it before constructing the request. --fail makes HTTP error responses fail the job rather than silently producing a file that happens to contain an error page. Do not print the token, enable shell tracing around authentication, or upload the token-bearing process environment as an artifact.
Before promoting the downloaded file, verify the expected archive structure and a digest or provenance record produced by the target pipeline. The HTTP request proves only that the endpoint returned a response. It does not prove that the artifact came from the approved commit, that it passed release checks, or that the source project was authorized by the right human.
Understand the allowlist’s limits
For a private target project, an allowlist entry and the triggering user’s target permissions both matter. For public or internal projects, some features can be accessed without an allowlist by default; project visibility settings can restrict individual features to members. GitLab Self-Managed administrators may also enforce allowlist policy across projects. Inspect the target’s actual configuration and instance policy instead of assuming the GitLab.com default.
GitLab’s fine-grained permissions for job tokens can further constrain selected REST API endpoints for allowlisted groups and projects. These endpoint permissions supplement the allowlist; they do not replace it, the triggering user’s authorization, or endpoint support for CI job tokens. GitLab documents fine-grained job-token permissions as generally available beginning with version 18.3. On older installations, or where the feature is not enabled, validate the supported behavior for that instance rather than copying current UI steps.
Token format is a separate upgrade concern: GitLab documents JWT-format CI job tokens as the default beginning in 19.0, with the legacy-format setting available only until 20.0. Check custom integrations for assumptions about token shape during an upgrade; changing serialization does not by itself grant a token new project permissions.
Do not disable the target’s allowlist to “fix” a 403 except as a tightly controlled, temporary diagnostic on an isolated test project. GitLab explicitly warns that doing so can let jobs from any source project attempt access, subject to the user permissions and target behavior. Restore the restriction immediately and test both the intended source and an unauthorized source.
Diagnose 401 and 403 responses methodically
When a job-token request fails, check the gates in order:
- Confirm the target project ID and URL resolve to the expected instance and project.
- Confirm the source project or its intended group is on the target’s inbound allowlist.
- Confirm the user who triggered this exact pipeline has the required target-project role.
- Confirm the endpoint supports
CI_JOB_TOKENauthentication and the request uses the documented header. - Check target visibility restrictions, instance-enforced allowlists, and any fine-grained endpoint permission.
- Confirm the job is still running; tokens are revoked after job completion and are not durable credentials.
Use a small permissions matrix: one approved source project with a qualified user; the same project triggered by a user lacking the target role; and a project outside the allowlist. The first should succeed only for the intended operation. The other two should fail without revealing sensitive project contents. Keep the response status, target project, source project, endpoint, and trigger identity in the test evidence, but never log the credential.
Keep the runner inside the threat model
Masking a job token in job logs is a safeguard, not a sandbox. Repository scripts, third-party CI components, dependency install hooks, and compromised runner hosts can still access credentials presented to a job. GitLab warns against insecure reuse patterns such as privileged Docker on reused machines and the shell executor on a shared host. Separate untrusted contribution jobs from credential-bearing jobs, use isolated or ephemeral runners where appropriate, restrict egress, and grant no unrelated secrets to the artifact-download stage.
If a job needs to push to another repository, do not infer that an artifact-read allowlist grants write access. GitLab’s repository-push settings are separate, are disabled by default in the documented flow, and have their own source/target and version conditions. Prefer a read-only artifact handoff when that satisfies the release design; add write capability only after testing the exact target instance’s supported policy and trigger behavior.
The reliable design treats CI_JOB_TOKEN as a job-scoped identity with target-side admission, user-derived authorization, endpoint-specific capability, and a runner exposure window. Document each relationship, test the positive and negative cases, and avoid broadening the allowlist or switching to a long-lived token just to make a pipeline green.
Related:
- GitLab Protected Environments: Deployment Access and Approval Boundaries
- GitLab CI Production Pipelines: DAG Dependencies, Artifact Contracts, and Deployment Locks
Sources: