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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions