Skip to content
Kenta Ishizaki edited this page Jun 15, 2026 · 1 revision

Routes

The installation generator will update your config/routes.rb to define all required routes:

Rails.application.routes.draw do
  use_doorkeeper_openid_connect
  # your routes
end

This will mount the following routes:

GET   /oauth/userinfo
POST  /oauth/userinfo
GET   /oauth/discovery/keys
GET   /.well-known/openid-configuration
GET   /.well-known/oauth-authorization-server
GET   /.well-known/webfinger

With the exception of the hard-coded /.well-known paths (see RFC 5785) you can customize routes in the same way as with Doorkeeper, please refer to this page on their wiki.

Customizing the jwks_uri path in the discovery document

discovery_url_options lets you tweak the host, protocol, or port of the published jwks_uri, but not the path itself. To advertise a custom path — while keeping /oauth/discovery/keys working for existing clients during a rollover — mount the discovery controller at the new path and re-point the oauth_discovery_keys_url helper at it via direct:

# config/routes.rb
Rails.application.routes.draw do
  use_doorkeeper_openid_connect

  # 1. Mount the custom path under a non-conflicting helper name
  get "/-/jwks",
      to: "doorkeeper/openid_connect/discovery#keys",
      as: :custom_jwks

  # 2. Re-point oauth_discovery_keys_url at the new path
  direct(:oauth_discovery_keys) { |opts| custom_jwks_url(opts) }
end

After this, .well-known/openid-configuration returns "jwks_uri": "https://example.com/-/jwks", and the original /oauth/discovery/keys route still responds (handy during a rollover).

Note

A naive match "/-/jwks", ..., as: :oauth_discovery_keys won't work — Rails has refused to reuse a route name since 4.0 and raises ArgumentError: Invalid route name, already in use: 'oauth_discovery_keys'. The direct helper sidesteps this by overriding the URL helper itself rather than re-declaring the route name.

Mounting under multiple namespaces (multiple resource owner models)

If your app authenticates more than one kind of resource owner (e.g. a User and a Customer Devise model) you may want to mount Doorkeeper — and this engine — more than once, each under its own namespace:

# config/routes.rb
Rails.application.routes.draw do
  scope :users, as: :users do
    use_doorkeeper { controllers authorizations: "users/authorizations" }
    use_doorkeeper_openid_connect
  end

  scope :customers, as: :customers do
    use_doorkeeper { controllers authorizations: "customers/authorizations" }
    use_doorkeeper_openid_connect
  end
end

Most of the request flow is model-agnostic: the resource_owner_authenticator block can dispatch on whichever owner is signed in (current_user || current_customer), and claims / userinfo / ID token generation follow from whatever it returns.

The one piece that needs attention is the discovery document. DiscoveryController#provider_response builds the published endpoints by calling named route helpers (oauth_authorization_url, oauth_token_url, …) directly. The discovery_url_options setting (added in #126) lets you override the host / protocol / port of those URLs, but not which named helper is resolved — so under multiple mounts every namespace's discovery document would point at the same set of endpoints.

The idiomatic fix is to subclass the discovery controller per namespace and re-point the helper calls at that namespace's routes:

# app/controllers/users/discovery_controller.rb
module Users
  class DiscoveryController < Doorkeeper::OpenidConnect::DiscoveryController
    private

    # Re-point each helper used by `provider_response` at the namespaced route.
    def oauth_authorization_url(opts = {})
      users_oauth_authorization_url(opts)
    end

    def oauth_token_url(opts = {})
      users_oauth_token_url(opts)
    end

    def oauth_revoke_url(opts = {})
      users_oauth_revoke_url(opts)
    end

    def oauth_userinfo_url(opts = {})
      users_oauth_userinfo_url(opts)
    end

    def oauth_discovery_keys_url(opts = {})
      users_oauth_discovery_keys_url(opts)
    end

    # ...and `oauth_introspect_url` / `oauth_dynamic_client_registration_url`
    # if you advertise those.
  end
end
# config/routes.rb (inside the `:users` scope)
get "/.well-known/openid-configuration",
    to: "users/discovery#provider", as: :users_openid_connect_config

Repeat for the customers namespace. Each .well-known/openid-configuration then advertises the endpoints for its own namespace.

Note

See #192 for the original discussion. First-class multi-mount support is not provided out of the box; the per-namespace controller override above is the supported extension pattern for now.

Clone this wiki locally