Skip to content

Add token validation (e.g. CI_JOB_TOKEN: probe /job) to improve auth diagnostics #3256

@stdedos

Description

@stdedos

Description of the problem, including code/CLI snippet

Generic 401/403s make CI_JOB_TOKEN troubleshooting slow. Users can’t easily tell if a token is a job token, private/OAuth, or just out of scope.

One may verify that their CI_JOB_TOKEN is valid via any of:

$ wget -O- --header "JOB-TOKEN: $CI_JOB_TOKEN" "${CI_API_V4_URL}/job" || :
Connecting to gitlab.xx_xx_xx.com (xx.xx.xx.xx:443)
writing to stdout
{"id":xxxxxxx,"status":"running","stage":"deploy","name":"xx_xxx","ref":"xxxxxx/xxxxxxx/debug-job-issue","tag":false,"coverage":null,"allow_failure":false,"created_at":"2025-08-25T08:45:12.045Z","started_at":"2025-08-25T08:45:21.130Z","finished_at":null,"erased_at":null,"duration":11.964642337,"queued_duration":8.933698,"user":{"id":xxxx,"username":"x","name":"x","state":"active","locked":false,"avatar_url":"https://secure.gravatar.com/avatar/x?s=80\u0026d=identicon","web_url":"https://gitlab.xx_xx_xx.com/x","created_at":"2022-04-11T07:53:06.941Z"},"commit":{"id":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","short_id":"xxxxxxxx","created_at":"2025-08-22T18:21:27.000+03:00","parent_ids":["xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"],"title":"debug locally","message":"debug locally\n\nSigned-off-by: x \u003cx@users.noreply.gitlab.com\u003e\n","author_name":"x","author_email":"x@users.noreply.gitlab.com","authored_date":"2025-08-22T16:51:15.000+03:00","committer_name":"x","committer_email":"x@users.noreply.gitlab.com","committed_date":"2025-08-22T18:21:27.000+03:00","web_url":"https://gitlab.xx_xx_xx.com/x-x/x-x/-/commit/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"},"pipeline":{"id":xxxxxxx,"iid":xx,"project_id":xxxx,"sha":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","ref":"xxxxxx/xxxxxxx/debug-job-issue","status":"running","source":"web","created_at":"2025-08-22T15:21:52.835Z","updated_at":"2025-08-25T08:45:12.236Z","web_url":"https://gitlab.xx_xx_xx.com/x-x/x-x/-/pipelines/xxxxxxx"},"web_url":"https://gitlab.xx_xx_xx.com/x-x/x-x/-/jobs/xxxxxxx","project":{"ci_job_token_scope_enabled":false},"artifacts":[],"runner":{"id":xxxxxx,"description":"global-small","active":true,"paused":false,"is_shared":true,"runner_type":"instance_type","name":"gitlab-runner","online":true,"status":"online"},"runner_manager":{"id":xxxxx,"system_id":"xxxxxxxxxxxxxx","version":"xx.xx.x","revision":"xxxxxxxx","platform":"linux","architecture":"amd64","created_at":"2025-08-21T00:15:32.832Z","contacted_at":"2025-08-25T08:45:32.806Z","ip_address":"xx.xxx.x.xxx","status":"online"},"artifacts_expire_at":null,"archived":false,"tag_list":["global-small"]}-                    100% |********************************|  2796  0:00:00 ETA
written to stdout

$ wget -O- --header "JOB-TOKEN: $CI_JOB_TOKEN" "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/environments" || :
Connecting to gitlab.xx_xx_xx.com (xx.xx.xx.xx:443)
writing to stdout
[]-                    100% |********************************|     2  0:00:00 ETA
written to stdout

$ wget -O- --header "JOB-TOKEN: $CI_JOB_TOKEN" "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/releases" || :
Connecting to gitlab.xx_xx_xx.com (xx.xx.xx.xx:443)
writing to stdout
[]-                    100% |********************************|     2  0:00:00 ETA
written to stdout

However, trying to use a CI_JOB_TOKEN (and failing) can have many causes. In my case, someone "impersonated" CI_JOB_TOKEN for some kind of token.
I think python-gitlab telling me "are you sure xyz is indeed a CI_JOB_TOKEN, and in-scope (owning job still needs to be running)?" would've led me to this conclusion faster.

Ofc, Gitlab does not "expose what kind of token X is" - but $CI_JOB_TOKEN /job is a good indicator for a job_token.

Additionally, you have:

# make an API request to create the gl.user object. This is not required but may be useful
# to validate your token authentication. Note that this will not work with job tokens.
gl.auth()

Validating/Working with a CI_JOB_TOKEN can be PITN - I think python-gitlab telling me "are you sure xyz is indeed a CI_JOB_TOKEN?" would've led me to resolution faster.

Note that GET /job is "immune" to fine-grained (job token) permissions (https://docs.gitlab.com/ci/jobs/fine_grained_permissions/)

Expected Behavior

My suggestion would be

Would you be open in incorporating e.g. gitlab.http_get("/job") inside gl.auth() when one is using the job_token=?
(I couldn't find an gitlab.job() endpoint, hence the #http_get())

Actual Behavior

Specifications

  • python-gitlab version:
  • Gitlab server version (or gitlab.com):

Additionals

Ofc, Gitlab does not "expose what kind of token X is" - but $CI_JOB_TOKEN /job is a good indicator for job_token. To be honest, I would've made a troubleshooting command for myself:

  • check /user [if token is private],
  • check /job [if token is CI_JOB_TOKEN],
  • moar logic here

to verify dev's/user's "claims" i.e. using gitlab.Gitlab(url, private_token=private_token), gitlab.Gitlab(url, job_token=job_token)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions