Fix OAuth Device Flow verification_uri ignoring relative_url_root
What does this MR do and why?
Closes #602380 (closed).
On an instance configured with a relative_url_root (e.g.
external_url 'https://host.example/gitlab/' in gitlab.rb), the
OAuth 2.0 Device Authorization Grant (RFC 8628) response's
verification_uri and verification_uri_complete omit the
/gitlab prefix, pointing device-flow clients at a URL that 404s.
Root cause
The doorkeeper-device_authorization_grant gem's default
verification_uri/verification_uri_complete procs
(lib/doorkeeper/device_authorization_grant/config.rb) build the URI
from a host_name the gem computes itself, purely from the raw
request:
# doorkeeper-device_authorization_grant gem
def host_name
req = server.context.request
"#{req.scheme}://#{req.host}#{port}"
endThis never accounts for relative_url_root — GitLab's own
config/initializers/default_url_options.rb already handles that via
script_name: Gitlab.config.gitlab.relative_url_root, but that only
applies to actual Rails route URL helpers, which the gem bypasses
entirely by string-concatenating host_name with a hardcoded
/oauth/device path.
Fix
config/initializers/doorkeeper_device_authorization_grant.rb
already had the override procs present but commented out as an
example. Enabled them, replacing the gem's raw host-based
construction with the actual route:
Gitlab::Routing.url_helpers.oauth_device_authorizations_index_url,
which resolves through the relative_url_root-aware
default_url_options GitLab already sets up everywhere else.
This matches the established convention for every other OAuth
discovery/metadata endpoint in this codebase — JwksController#provider
(.well-known/oauth-authorization-server) and the
Doorkeeper::OpenidConnect::DiscoveryController superclass it inherits
from both advertise the canonically-configured URL rather than the
request host, and GitLab's own doorkeeper_openid_connect.rb
initializer pins issuer Gitlab.config.gitlab.url on the same basis.
Screenshots or screen recordings
N/A — backend-only change.
How to set up and validate locally
Only spec/requests/oauth/flows/device_grant_spec.rb changed for this
fix (new relative_url_root context asserting both verification_uri
and verification_uri_complete include the configured prefix).
spec/controllers/oauth/device_authorizations_controller_spec.rb is
included below only as a regression check, since it exercises the same
controller family:
bin/rspec spec/requests/oauth/flows/device_grant_spec.rb spec/controllers/oauth/device_authorizations_controller_spec.rbManual verification on GDK (no gitlab.yml edit or restart needed —
this simulates relative_url_root live in a bin/rails console, the same
way the spec above stubs it):
Gitlab::Application.routes.default_url_options =
Gitlab::Application.routes.default_url_options.merge(script_name: '/gitlab')
verification_uri = Doorkeeper::DeviceAuthorizationGrant.configuration.verification_uri.call(nil)
puts verification_uri
# => http://127.0.0.1:3000/gitlab/oauth/device
device_grant = Doorkeeper::DeviceAuthorizationGrant::DeviceGrant.new(user_code: 'ABCD-1234')
puts Doorkeeper::DeviceAuthorizationGrant.configuration.verification_uri_complete.call(verification_uri, nil, device_grant)
# => http://127.0.0.1:3000/gitlab/oauth/device?user_code=ABCD-1234Output above is real, captured on this branch on a local GDK.
Disclosure
This fix was drafted with AI assistance (Claude Code). Root cause was
traced into the doorkeeper-device_authorization_grant gem's source
(installed gem, not vendored) to confirm exactly how host_name is
built, then verified against GitLab's own default_url_options
initializer. The fix and tests were AI-assisted, verified via a full
local run of the affected spec files (25 examples, 0 failures) rather
than taken on faith.