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:8080and the IAM service ishttp://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.