Fall back to IAM on a Rails OAuth client lookup miss

Related to #602404

Closes a part of #623577

What?

Rails oauth_routing returns iam when the client_id is missing or not in the Rails database, so Workhorse sends those /oauth/authorize and /oauth/authorize_device requests to the IAM Auth service.

Why?

Workhorse routes OAuth traffic to IAM only when it is started with IAM_SERVICE_URL, the switch !236231 (merged) added for the AUTH-011 Workhorse rollout (#594504 (comment 3493702545)). With it set, Workhorse asks this endpoint where each /oauth/authorize goes.

A dynamically registered client is created on IAM and has no Rails row, so the lookup misses it and Doorkeeper answers invalid_client. Per #623577 (comment 3903147872) the miss falls back to IAM, the fewest changes, replacing the client_id prefix in gitlab-org/auth/iam!700 (closed).

This is the interim path. At the Cloudflare cutover an Origin Rule sends /oauth/* to IAM before Workhorse asks Rails, per AUTH-011 Phase 5.

Screenshots & Recordings

Scenario Demo
Unknown client_id, routed to IAM
Rails application, stays on Rails
No client_id, routed to IAM

How to set up and validate locally

Note: In the steps below, GDK is https://gdk.test:8080 and the IAM service is http://gdk.test:8084. Adjust the URLs to match your local configuration.

Steps

1. Start IAM

cd ~/iam && mise exec -- make iam-auth && mise exec -- make run-auth
# {"component":"auth_http","level":"info","msg":"listening on :8084"}

2. Register a client on Rails, routing still off

curl -X POST https://gdk.test:8080/oauth/register -H 'Content-Type: application/json' \
  -d '{"client_name":"Rails client","redirect_uris":["http://localhost:1234/cb"]}'
# {"client_id":"<...>",...,"grant_types":["authorization_code"],"require_pkce":true,...,"dynamic":true}

RAILS_CLIENT_ID=""

3. Point Workhorse at IAM

cd ~/gitlab-development-kit
echo 'export IAM_SERVICE_URL=http://gdk.test:8084' >> env.runit
mise exec -- gdk restart gitlab-workhorse rails-web
grep 'OAuth routing to IAM service enabled' log/gitlab-workhorse/current | tail -1
# {"msg":"oauthproxy: OAuth routing to IAM service enabled","iam_routing_enabled":true,"iam_backend_url":"http://gdk.test:8084"}

4. Register a client on IAM

curl -X POST http://gdk.test:8084/oauth/register -H 'Content-Type: application/json' \
  -d '{"client_name":"IAM client","redirect_uris":["http://localhost:1234/cb"]}'
# {"client_id":"<...>","client_id_issued_at":<...>,"redirect_uris":["http://localhost:1234/cb"],"token_endpoint_auth_method":"none","grant_types":["authorization_code","refresh_token"],"client_name":"[Unverified Dynamic Application] IAM client","scope":"mcp"}

IAM_CLIENT_ID=""
AUTHZ='https://gdk.test:8080/oauth/authorize?response_type=code&redirect_uri=http://localhost:1234/cb&scope=mcp&state=dcr-routing-test&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256'

5. Unknown to Rails, goes to IAM

curl -i "$AUTHZ&client_id=$IAM_CLIENT_ID"
# HTTP/1.1 302 Found
# Location: http://localhost:3000/users/sign_in?login_challenge=<64 hex chars>   (IAM's login_url)

grep 'oauthproxy: routing decision' log/gitlab-workhorse/current | tail -1
# "path":"/oauth/authorize","destination":"iam","reason":"iam_routing_enabled"

6. Known to Rails, stays on Rails

curl -i "$AUTHZ&client_id=$RAILS_CLIENT_ID"
# HTTP/1.1 302 Found
# Location: https://gdk.test:3443/users/sign_in   (Rails external_url)

grep 'oauthproxy: routing decision' log/gitlab-workhorse/current | tail -1
# "path":"/oauth/authorize","destination":"rails","reason":"doorkeeper_ff_disabled"

7. No client_id, goes to IAM

curl -i "$AUTHZ"
# HTTP/1.1 401 Unauthorized
# {"error":"invalid_client","error_description":"Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The requested OAuth 2.0 Client does not exist."}

grep 'oauthproxy: routing decision' log/gitlab-workhorse/current | tail -1
# "path":"/oauth/authorize","destination":"iam","reason":"iam_routing_enabled"

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Hakeem Abdul-Razak

Merge request reports

Loading
Loading