GitLab does not process JWT token

Summary

A customer has implemented an Auth Service (javascript) service that validates and passes a JWT Claim to GitLab, which is [in leu of the completion of this feature request to support authentication through Cloudflare access. The customers service is getting/validating the JWT payload and then is attempting use the claim against GitLab for authentication.Unfortunately, GitLab doesn't appear to be doing anything with the JWT claim that is passed by the external service to GitLab.

Gitlab responds with a code 200, then a 302 with an "Ignoring the response-body" message, followed by a code 200 again at the login screen.

Steps to reproduce

Customer has a proprietary auth service (javascript) service that validates and passes a JWT Claim to GitLab, which is in leu of the completion of this feature request to support authentication through Cloudflare access. The customers service is getting/validating the JWT payload and then is attempting use the claim against GitLab for authentication.Unfortunately, GitLab doesn't appear to be doing anything with the JWT claim that is passed by the external service to GitLab.

After setting up JWT as your OmniAuth provider:

  1. Obtain a JWT claim from Cloudflare.
  2. Pass the RS256 JWT claim to gitlab and get redirected to login page.

What is the current bug behavior?

The JWT claim is passed to the endpoint of /auth/jwt/callback?jwt=RS256-REDACTED or /users/auth/jwt/callback?jwt=RS256-REDACTED as a GET. Instead of being authenticated, gitlab responds with a code 200, then a 302 with an "Ignoring the response-body" message, followed by a code 200 again at the login screen.

What is the expected correct behavior?

The expected behavior is that with JWT set as the OmniAuth provider, whether manually or through a service, a user should be able to pass the JWT claim to the GitLab and become authenticated.

Relevant logs and/or screenshots

Expand for sanitized curl output
~  curl -v --location --request GET 'https://gitlab.example.com/auth/jwt/callback?jwt=RS256-REDACTED' \
    --header 'CF-Access-Client-ID: REDACTED.access' \
    --header 'CF-Access-Client-Secret: REDACTED' \
    --header 'Cookie: __cfduid=REDACTED; experimentation_subject_id=RS256-REDACTED; _gitlab_session=REDACTED'
Note: Unnecessary use of -X or --request, GET is already inferred.
*   Trying 123.xx.xx.xxx...
* TCP_NODELAY set
* Connected to gitlab.example.com (123.xx.xx.xxx...) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pem
 CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Request CERT (13):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Certificate (11):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-ECDSA-CHACHA20-POLY1305
* ALPN, server accepted to use h2
* Server certificate:
*  subject: C=US; ST=CA; L=San Francisco; O=Cloudflare, Inc.; CN=example.com
*  start date: Jun 10 00:00:00 2020 GMT
*  expire date: Jun 10 12:00:00 2021 GMT
*  subjectAltName: host "cicd1.example.com" matched cert's "*.cicd2.example.com"
*  issuer: C=US; O=Cloudflare, Inc.; CN=Cloudflare Inc ECC CA-3
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7fe4f100a200)
> GET /auth/jwt/callback?jwt=RS256-REDACTED HTTP/2
> Host: gitlab.example.com
> User-Agent: curl/7.54.0
> Accept: */*
> CF-Access-Client-ID: REDACTED.access
> CF-Access-Client-Secret: REDACTED
> Cookie: __cfduid=REDACTED; experimentation_subject_id=RS256-REDACTED--REDACTED; CF_Authorization="RS256-REDACTED"; _gitlab_session=REDACTED
>
* Connection state changed (MAX_CONCURRENT_STREAMS updated)!
< HTTP/2 302
< date: Mon, 27 Jul 2020 18:52:15 GMT
< content-type: text/html; charset=utf-8
< cache-control: no-cache
< location: https://gitlab.example.com/users/sign_in
< referrer-policy: strict-origin-when-cross-origin
< set-cookie: _gitlab_session=REDACTED; path=/; expires=Mon, 27 Jul 2020 20:52:15 -0000; secure; HttpOnly; SameSite=None
< x-content-type-options: nosniff
< x-download-options: noopen
< x-frame-options: DENY
< x-permitted-cross-domain-policies: none
< x-request-id: kSnWFL7yJU1
< x-runtime: 0.112891
< x-ua-compatible: IE=edge
< x-xss-protection: 1; mode=block
< cf-cache-status: DYNAMIC
< cf-request-id: 0433375a5e0000091dd6828200000001
< expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
< server: cloudflare
< cf-ray: 5b988e709b34091d-SEA
<
* Ignoring the response-body
* Connection #0 to host gitlab.example.com left intact
* Issue another request to this URL: 'https://gitlab.example.com/users/sign_in'
* Found bundle for host gitlab.example.com: 0x7fe4f0c18960 [can multiplex]
* Re-using existing connection! (#0) with host gitlab.example.com
* Connected to gitlab.example.com (123.xx.xx.xxx) port 443 (#0)
* Using Stream ID: 3 (easy handle 0x7fe4f100a200)
> GET /users/sign_in HTTP/2
> Host: gitlab.example.com
> User-Agent: curl/7.54.0
> Accept: */*
> CF-Access-Client-ID: REDACTED.access
> CF-Access-Client-Secret: REDACTED
> Cookie: __cfduid=REDACTED; experimentation_subject_id=RS256-REDACTED--REDACTED; CF_Authorization=RS256-REDACTED; _gitlab_session=REDACTED
>
< HTTP/2 200
< date: Mon, 27 Jul 2020 18:52:16 GMT
< content-type: text/html; charset=utf-8
< cache-control: max-age=0, private, must-revalidate
< referrer-policy: strict-origin-when-cross-origin
< set-cookie: _gitlab_session=c1aebd5c189ca24365185e738da2298a; path=/; expires=Mon, 27 Jul 2020 20:52:16 -0000; secure; HttpOnly; SameSite=None
< x-content-type-options: nosniff
< x-download-options: noopen
< x-frame-options: DENY
< x-permitted-cross-domain-policies: none
< x-request-id: Ytxu0NEu5f
< x-runtime: 0.043765
< x-ua-compatible: IE=edge
< x-xss-protection: 1; mode=block
< cf-cache-status: DYNAMIC
< cf-request-id: 0433375d7f0000091dd685c200000001
< expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
< server: cloudflare
< cf-ray: 5b988e759dcf091d-SEA
<
<!DOCTYPE html>
<html class="devise-layout-html">
<head prefix="og: http://ogp.me/ns#">
<meta charset="utf-8">
<meta content="IE=edge" http-equiv="X-UA-Compatible">
<meta content="object" property="og:type">
<meta content="GitLab" property="og:site_name">
<meta content="Sign in" property="og:title">
<meta content="GitLab Enterprise Edition" property="og:description">
<meta content="https://gitlab.example.com/assets/gitlab_logo-7ae504fe4f68fdebb3c2034e36621930cd36ea87924c11ff65dbcb8ed50dca58.png" property="og:image">
<meta content="64" property="og:image:width">
<meta content="64" property="og:image:height">
<meta content="https://gitlab.example.com/users/sign_in" property="og:url">
<meta content="summary" property="twitter:card">
<meta content="Sign in" property="twitter:title">
<meta content="GitLab Enterprise Edition" property="twitter:description">
<meta content="https://gitlab.example.com/assets/gitlab_logo-7ae504fe4f68fdebb3c2034e36621930cd36ea87924c11ff65dbcb8ed50dca58.png" property="twitter:image">

<title>Sign in · GitLab</title>
<meta content="GitLab Enterprise Edition" name="description">
<link rel="shortcut icon" type="image/png" href="/assets/favicon-7901bd695fb93edb07975966062049829afb56cf11511236e61bcf425070e36e.png" id="favicon" data-original-href="/assets/favicon-7901bd695fb93edb07975966062049829afb56cf11511236e61bcf425070e36e.png" />
<link rel="stylesheet" media="all" href="/assets/application-ae0edd232df6f579e19ea52115d35977f8bdbfa9958e0aef2221d62f3a39e7d8.css" />
<link rel="stylesheet" media="print" href="/assets/print-74c3df10dad473d66660c828e3aa54ca3bfeac6d8bb708643331403fe7211e60.css" />


<link rel="stylesheet" media="all" href="/assets/highlight/themes/white-a20fa0d18cb98944b079c02ad5a6f46cb362f986ffd703fda24b3e8e2a4a8874.css" />
<script>
//<![CDATA[
window.gon={};
//]]>
</script>


<script src="/assets/webpack/runtime.d005afc1.bundle.js" defer="defer"></script>
<script src="/assets/webpack/main.b1b0379f.chunk.js" defer="defer"></script>
<script src="/assets/webpack/commons~pages.admin.sessions~pages.groups.omniauth_callbacks~pages.ldap.omniauth_callbacks~pages.omn~2f65d76a.5c6932f7.chunk.js" defer="defer"></script>
<script src="/assets/webpack/pages.sessions.new.55801ed7.chunk.js" defer="defer"></script>

<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="REDACTED==" />

<meta name="action-cable-url" content="/-/cable" />
<meta content="origin-when-cross-origin" name="referrer">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<meta content="#474D57" name="theme-color">
<link rel="apple-touch-icon" type="image/x-icon" href="/assets/touch-icon-iphone-5a9cee0e8a51212e70b90c87c12f382c428870c0ff67d1eb034d884b78d2dae7.png" />
<link rel="apple-touch-icon" type="image/x-icon" href="/assets/touch-icon-ipad-a6eec6aeb9da138e507593b464fdac213047e49d3093fc30e90d9a995df83ba3.png" sizes="76x76" />
<link rel="apple-touch-icon" type="image/x-icon" href="/assets/touch-icon-iphone-retina-72e2aadf86513a56e050e7f0f2355deaa19cc17ed97bbe5147847f2748e5a3e3.png" sizes="120x120" />
<link rel="apple-touch-icon" type="image/x-icon" href="/assets/touch-icon-ipad-retina-8ebe416f5313483d9c1bc772b5bbe03ecad52a54eba443e5215a22caed2a16a2.png" sizes="152x152" />
<link color="rgb(226, 67, 41)" href="/assets/logo-d36b5212042cebc89b96df4bf6ac24e43db316143e89926c0db839ff694d2de4.svg" rel="mask-icon">
<meta content="/assets/msapplication-tile-1196ec67452f618d39cdd85e2e3a542f76574c071051ae7effbfde01710eb17d.png" name="msapplication-TileImage">
<meta content="#30353E" name="msapplication-TileColor">




</head>

<body class="application gl-browser-generic gl-platform-other login-page navless ui-indigo" data-page="sessions:new" data-qa-selector="login_page">

<script>
//<![CDATA[
gl = window.gl || {};
gl.client = {"isGeneric":true,"isOther":true};


//]]>
</script>
<div class="page-wrap">
<header class="navbar fixed-top navbar-empty">
<svg width="24" height="24" class="tanuki-logo" viewBox="0 0 36 36">
 <path class="tanuki-shape tanuki-left-ear" fill="#e24329" d="M2 14l9.38 9v-9l-4-12.28c-.205-.632-1.176-.632-1.38 0z"/>
 <path class="tanuki-shape tanuki-right-ear" fill="#e24329" d="M34 14l-9.38 9v-9l4-12.28c.205-.632 1.176-.632 1.38 0z"/>
 <path class="tanuki-shape tanuki-nose" fill="#e24329" d="M18,34.38 3,14 33,14 Z"/>
 <path class="tanuki-shape tanuki-left-eye" fill="#fc6d26" d="M18,34.38 11.38,14 2,14 6,25Z"/>
 <path class="tanuki-shape tanuki-right-eye" fill="#fc6d26" d="M18,34.38 24.62,14 34,14 30,25Z"/>
 <path class="tanuki-shape tanuki-left-cheek" fill="#fca326" d="M2 14L.1 20.16c-.18.565 0 1.2.5 1.56l17.42 12.66z"/>
 <path class="tanuki-shape tanuki-right-cheek" fill="#fca326" d="M34 14l1.9 6.16c.18.565 0 1.2-.5 1.56L18 34.38z"/>
</svg>

</header>

<div class="login-page-broadcast">


</div>
<div class="container navless-container">
<div class="content">
<div class="flash-container flash-container-page sticky">
<div class="flash-alert mb-2">
<svg class="s16 align-middle mr-1"><use xlink:href="/assets/icons-f0dc26411f6d9ceb1d30eab71475c17cca53e166e5c0d192565c1a25af40502e.svg#error"></use></svg>
<span>You need to sign in or sign up before continuing.</span>
<div class="close-icon-wrapper js-close-icon">
<svg class="s16 close-icon"><use xlink:href="/assets/icons-f0dc26411f6d9ceb1d30eab71475c17cca53e166e5c0d192565c1a25af40502e.svg#close"></use></svg>
</div>
</div>
</div>

<div class="row mt-3">
<div class="col-sm-12">
<h1 class="mb-3 font-weight-normal">
GitLab Enterprise Edition
</h1>
</div>
</div>
<div class="row mb-3">
<div class="col-sm-7 order-12 order-sm-1 brand-holder">

<h3 class="mt-sm-0">
A complete DevOps platform
</h3>
<p>
GitLab is a single application for the entire software development lifecycle. From project planning and source code management to CI/CD, monitoring, and security.
</p>

</div>
<div class="col-sm-5 order-1 order-sm-12 new-session-forms-container">
<div id="signin-container">
<ul class="nav-links new-session-tabs nav-tabs nav" role="tablist">
<li class="nav-item" role="presentation">
<a class="nav-link active" data-qa-selector="sign_in_tab" data-toggle="tab" href="#login-pane" role="tab">Sign in</a>
</li>
<li class="nav-item" role="presentation">
<a class="nav-link" data-qa-selector="register_tab" data-toggle="tab" data-track-event="click_button" data-track-label="sign_in_register" data-track-property="" data-track-value="" href="#register-pane" role="tab">Register</a>
</li>
</ul>

<div class="tab-content">
<div class="login-box tab-pane active" id="login-pane" role="tabpanel">
<div class="login-body">
<form class="new_user gl-show-field-errors" id="new_user" aria-live="assertive" action="/users/sign_in" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="&#x2713;" /><input type="hidden" name="authenticity_token" value="REDACTED==" /><div class="form-group">
<label for="user_login" class="label-bold">Username or email</label>
<input class="form-control top" autofocus="autofocus" autocapitalize="off" autocorrect="off" required="required" title="This field is required." data-qa-selector="login_field" type="text" name="user[login]" id="user_login" />
</div>
<div class="form-group">
<label class="label-bold" for="user_password">Password</label>
<input class="form-control bottom" required="required" title="This field is required." data-qa-selector="password_field" type="password" name="user[password]" id="user_password" />
</div>
<div class="remember-me">
<label for="user_remember_me">
<input name="user[remember_me]" type="hidden" value="0" /><input class="remember-me-checkbox" type="checkbox" value="1" name="user[remember_me]" id="user_remember_me" />
<span>Remember me</span>
</label>
<div class="float-right">
<a href="/users/password/new">Forgot your password?</a>
</div>
</div>
<div></div>
<div class="submit-container move-submit-down">
<input type="submit" name="commit" value="Sign in" class="btn btn-success" data-qa-selector="sign_in_button" data-disable-with="Sign in" />
</div>
</form>
</div>
</div>

<div class="tab-pane login-box" id="register-pane" role="tabpanel">
<div class="login-body">
<form class="new_new_user gl-show-field-errors" id="new_new_user" aria-live="assertive" action="/users" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="&#x2713;" /><input type="hidden" name="authenticity_token" value="REDACTED==" /><div class="devise-errors">

</div>
<div class="name form-group">
<label class="label-bold" for="new_user_name">Full name</label>
<input class="form-control top js-block-emoji js-validate-length" data-max-length="255" data-max-length-message="Name is too long (maximum is 255 characters)." data-qa-selector="new_user_name_field" required="required" title="This field is required." type="text" name="new_user[name]" id="new_user_name" />
</div>
<div class="username form-group">
<label class="label-bold" for="new_user_username">Username</label>
<input class="form-control middle js-block-emoji js-validate-length js-validate-username" data-max-length="255" data-max-length-message="Username is too long (maximum is 255 characters)." data-qa-selector="new_user_username_field" pattern="[a-zA-Z0-9_\.][a-zA-Z0-9_\-\.]*[a-zA-Z0-9_\-]|[a-zA-Z0-9_]" required="required" title="Please create a username with only alphanumeric characters." type="text" name="new_user[username]" id="new_user_username" />
<p class="validation-error gl-field-error-ignore field-validation hide">Username is already taken.</p>
<p class="validation-success gl-field-error-ignore field-validation hide">Username is available.</p>
<p class="validation-pending gl-field-error-ignore field-validation hide">Checking username availability...</p>
</div>
<div class="form-group">
<label class="label-bold" for="new_user_email">Email</label>
<input class="form-control middle" data-qa-selector="new_user_email_field" required="required" title="Please provide a valid email address." type="email" value="" name="new_user[email]" id="new_user_email" />
</div>
<div class="form-group">
<label class="label-bold" for="new_user_email_confirmation">Email confirmation</label>
<input class="form-control middle" data-qa-selector="new_user_email_confirmation_field" required="required" title="Please retype the email address." type="email" name="new_user[email_confirmation]" id="new_user_email_confirmation" />
</div>
<div class="form-group append-bottom-20" id="password-strength">
<label class="label-bold" for="new_user_password">Password</label>
<input class="form-control bottom" data-qa-selector="new_user_password_field" required="required" pattern=".{8,}" title="Minimum length is 8 characters." type="password" name="new_user[password]" id="new_user_password" />
<p class="gl-field-hint text-secondary">Minimum length is 8 characters</p>
</div>

<div></div>
<div class="submit-container">
<input type="submit" name="commit" value="Register" class="btn-register btn" data-qa-selector="new_user_register_button" data-disable-with="Register" />
</div>
</form></div>
</div>

</div>
<div class="clearfix">
<div class="omniauth-container prepend-top-15">
<label class="label-bold d-block">
Sign in with
</label>
<div class="d-flex justify-content-between flex-wrap">
<a class="btn d-flex align-items-center omniauth-btn text-left oauth-login " id="oauth-login-jwt" rel="nofollow" data-method="post" href="/users/auth/jwt"><span>
Cloudflare Access
</span>
</a></div>
<fieldset class="remember-me">
<label>
<input type="checkbox" name="remember_me" id="remember_me" class="remember-me-checkbox" />
<span>
Remember me
</span>
</label>
</fieldset>
</div>

</div>
</div>

</div>
</div>
</div>
</div>
<hr class="footer-fixed">
<div class="container footer-container">
<div class="footer-links">
<a href="/explore">Explore</a>
<a href="/help">Help</a>
<a href="https://about.gitlab.com/">About GitLab</a>
</div>
</div>

</div>
</body>
</html>
* Connection #0 to host gitlab.example.com left intact

Results of GitLab environment info

Expand for output related to GitLab environment info

(For installations with omnibus-gitlab package run and paste the output of:
`sudo gitlab-rake gitlab:env:info`)

(For installations from source run and paste the output of:
`sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`)

Results of GitLab application Check

Expand for output related to the GitLab application check

(For installations with omnibus-gitlab package run and paste the output of: sudo gitlab-rake gitlab:check SANITIZE=true)

(For installations from source run and paste the output of: sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production SANITIZE=true)

(we will only investigate if the tests are passing)

Possible fixes

#25958

(If you can, link to the line of code that might be responsible for the problem)

Edited by Gabriel Yoachum