Pointers on using Gitlab's CI_JOB_JWT to authenticate to realm

Gitlab CI jobs create a signed JWT exposed as an environment variable to each job.

I’d like to use this JWT (properly validated against the Gitlab jwks endpoint), to authenticate to a Keycloak realm so that I may use the resulting Keycloak JWT against other services (specifically, a Kubernetes cluster setup for oidc auth with my Keycloak server, AWS via AssumeRoleWithWebIdentity).

Essentially, I want Keycloak to “vouch” for the validity of the Gitlab JWT token, and issue a Keycloak JWT token, mapping some of the Gitlab JWT’s claims in the Keycloak JWT.

I have validated the Gitlab CI JWT token independently, so I assume it works as expected.

I have a Keycloak realm setup, and a client with credential authenticator set to “Signed JWT”, and the corresponding Gitlab JWKS endpoint. Additionally, the client “Access Type” is set to “Bearer-only”.

But, I’m not quite sure how to get a Keycloak JWT using the Gitlab JWT as a credential. I’m trying this all via curl:

$ curl -i -X POST -H "Content-Type: application/x-www-form-urlencoded" https://mykeycloak/auth/realms/gitlab/protocol/openid-connect/token -d 'grant_type=urn:ietf:params:oauth:grant-type:uma-ticket' -d "client_id=gitlab-ci-jwt-token" -d "claim_token_format=http://openid.net/specs/openid-connect-core-1_0.html#IDToken" -d "claim_token=$THETOKEN" -d "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" -d "client_assertion=$THETOKEN"

HTTP/2 400 
cache-control: no-store
x-xss-protection: 1; mode=block
pragma: no-cache
x-frame-options: SAMEORIGIN
referrer-policy: no-referrer
date: Wed, 24 Feb 2021 20:07:22 GMT
strict-transport-security: max-age=31536000; includeSubDomains
x-content-type-options: nosniff
content-type: application/json
content-length: 80
x-envoy-upstream-service-time: 7
server: istio-envoy

{"error":"unauthorized_client","error_description":"Invalid client credentials"}

Here is the output from the Keycloak server:

20:11:55,017 WARN  [org.keycloak.events] (default task-53) type=PERMISSION_TOKEN_ERROR, realmId=gitlab, clientId=job_3299534, userId=null, ipAddress=, error=client_not_found, auth_method=oauth_credentials, grant_type=urn:ietf:params:oauth:grant-type:uma-ticket

It looks like clientId is getting set from the GitlabCI JWT sub field despite using -d "client_id=gitlab-ci-jwt-token" in the curl request.

I’d appreciate any pointers on this, including any suggestions/docs on faulty assumptions or obvious errors here.


Do I need to add a custom Identity Provider here?

Hi @stensonb,

did you succeed meanwhile? I just wanted to implement something like this (as well using the CI_JOB_JWT)

Best regards

Hi Mirko -

I did not end up using Keycloak, but built a system which takes gitlab tokens, and mints new tokens (which we setup tibhave external systems trust).

FYI, I also added a feature proposal here, which didn’t get much traction at the time, but seems to be a feature some of gitlab’s customers are asking for: https://gitlab.com/gitlab-org/gitlab/-/issues/294291

Good luck!

Thanks for the answer. I had developed a specialized token exchanger myself but thought it would come handy to “just configure” Keycloak (though this is sometimes more complicated than just programming a microservice in Java :sweat_smile:)