Doorkeeper: Errors while integrating Doorkeeper in a MultiEngine Rails Application

Created on 15 Apr 2018  路  7Comments  路  Source: doorkeeper-gem/doorkeeper

This is our app structure

app
|
|__engines
|
|___ api
|___ other engine

main app route.rb
mount api::engine at: '/api'

engine api route
use_doorkeeper and other mount from grape


Currently we have a multi engine application in rails. Where there is a Grape based API which we need to authorize using the Doorkeeper. As we are using isolated namespaces. There are some instructions on how to do that in different threads. however each has their unique errors. We want these routes working

| link | method | path | controller_action |
| ----- | -------- | ------ | ------------------ |
| oauth_authorization | GET | /oauth/authorize(.:format) | api/doorkeeper/authorizations#show |
| for other methods | GET | /oauth/authorize/:code(.:format) | api/doorkeeper/authorizations#new |
| for other methods | DELETE | /oauth/authorize(.:format) | api/doorkeeper/authorizations#destroy |
| for other methods | POST | /oauth/authorize(.:format) | api/doorkeeper/authorizations#create |
| oauth_token | POST | /oauth/token(.:format) | api/doorkeeper/tokens#create |
| oauth_revoke | POST | /oauth/revoke(.:format) | api/doorkeeper/tokens#revoke |
| oauth_applications | GET | /oauth/applications(.:format) | api/doorkeeper/applications#index |
| for other methods | POST | /oauth/applications(.:format) | api/doorkeeper/applications#create |
| new_oauth_application | GET | /oauth/applications/new(.:format) | api/doorkeeper/applications#new |
| edit_oauth_application | GET | /oauth/applications/:id/edit(.:format) | api/doorkeeper/applications#edit |
| oauth_application | GET | /oauth/applications/:id(.:format) | api/doorkeeper/applications#show |
| for other methods | PATCH | /oauth/applications/:id(.:format) | api/doorkeeper/applications#update |
| for other methods | PUT | /oauth/applications/:id(.:format) | api/doorkeeper/applications#update |
| for other methods | DELETE | /oauth/applications/:id(.:format) | api/doorkeeper/applications#destroy |
| oauth_authorized_applications | GET | /oauth/authorized_applications(.:format) | api/doorkeeper/authorized_applications#index |
| oauth_authorized_application | DELETE | /oauth/authorized_applications/:id(.:format) | api/doorkeeper/authorized_applications#destroy |
| oauth_token_info | GET | /oauth/token/info(.:format) | api/doorkeeper/token_info#show |

Solution 1
Adding this
API::Doorkeeper = ::Doorkeeper in initializers folder or lib folder as api.rb (considering API is the namespace)

Doorkeeper.configure [do](url)
  orm :active_record
  API::Doorkeeper = ::Doorkeeper
.
.
.
end

it works for
/oauth/token and /oauth/token/info. Any routes related to views gives errors similar to this

undefined local variable or method `new_oauth_application_path'

as described in a Stackoverflow thread adding the following code in the initializer does not help in
/oauth/applications and /oauth/authorized_applications routes
::Doorkeeper::ApplicationController.send(:include, API::Engine.routes.url_helpers)

Solution 2
if we override all the controllers of Doorkeeper in /gems/doorkeeper-4.2.5/app/controllers/doorkeeper in api/controllers/doorkeeper in our engine

application_controller.rb               
applications_controller.rb              
authorized_applications_controller.rb   
tokens_controller.rb
application_metal_controller.rb         
authorizations_controller.rb            
token_info_controller.rb

like this

module Admin
  module Doorkeeper
    class ApplicationsController < ::Admin::ApplicationController
      load_and_authorize_resource class: '::Doorkeeper::Application'

      def index
        @applications = ::Doorkeeper::Application.all
      end

      def new
        @application = ::Doorkeeper::Application.new
      end
.
.
.
.
end

all the routes work but we need to change all the namespaces accordingly in order to make it work.

Solution 3
Apply 1 and 2 simultaneously by only overriding the classes that we use and add the constant in the initializer. However in this case the problem is we might get random error if the routes are hit.

So in my humble opinion all three options has their merit and demerit. Which one should be more acceptable as security practice. Thank you. I did not find any wiki on this. But there are many threads in stack and here from those who faced similar issues. But there seems to be no concrete answer.

enhancement help wanted

Most helpful comment

Came up with a workaround that is fine for my use case. Basically, draw routes in the root application from within the Api engine. Example:

#engines/api/config/routes.rb

Rails.application.routes.draw do
  use_doorkeeper scope: 'api/v1/oauth'
end

Api::Engine.routes.draw do
  scope 'v1' do
    # my other engine routes here
  end
end

Didn't realize an Engine can add to it's parent applications routes if it wants to. Pretty useful! Haven't found any obvious issues yet.

All 7 comments

Hi @yra-wtag . There are no concrete answer or Wiki page for this case. If you have some experience on using Doorkeeper in multi-engine applications and you have a proper solution - you can send a PR to improve Doorkeeper gem, we can discuss it. I think we could need a sample application in order to check out the problems of such integration. Thanks!

Came up with a workaround that is fine for my use case. Basically, draw routes in the root application from within the Api engine. Example:

#engines/api/config/routes.rb

Rails.application.routes.draw do
  use_doorkeeper scope: 'api/v1/oauth'
end

Api::Engine.routes.draw do
  scope 'v1' do
    # my other engine routes here
  end
end

Didn't realize an Engine can add to it's parent applications routes if it wants to. Pretty useful! Haven't found any obvious issues yet.

Thanks @j1mmie . I think we can create a standalone Wiki page for this case and post your answer for those who will search it. WDYT?

That's a great idea. I'm happy to add it myself, but could use a suggestion on where. Otherwise, feel free to create the wiki page yourself.

Thanks @j1mmie . You can extend this page

I'm closing this stale issue. If @yra-wtag @j1mmie have some useful info to share, you can extend the same-named Wiki page. Thanks!

I have a similar problem.

I use the following routes:

scope module: :api, path: 'api' do
  scope module: :v1, path: 'v1' do
    use_doorkeeper
  end
end

And I end up with: _NameError - uninitialized constant Api::V1::Doorkeeper:_

Why? Should some parts in the code be changed from Doorkeeper to ::Doorkeeper?

Update

scope module adds modules to doorkeeper controllers.

                   Prefix Verb   URI Pattern                                Controller#Action
native_oauth_authorization GET   /api/v1/oauth/authorize/native(.:format)   api/v1/doorkeeper/authorizations#show {:format=>:json}
      oauth_authorization GET    /api/v1/oauth/authorize(.:format)          api/v1/doorkeeper/authorizations#new {:format=>:json}
                          DELETE /api/v1/oauth/authorize(.:format)          api/v1/doorkeeper/authorizations#destroy {:format=>:json}
                          POST   /api/v1/oauth/authorize(.:format)          api/v1/doorkeeper/authorizations#create {:format=>:json}
              oauth_token POST   /api/v1/oauth/token(.:format)              api/v1/doorkeeper/tokens#create {:format=>:json}
             oauth_revoke POST   /api/v1/oauth/revoke(.:format)             api/v1/doorkeeper/tokens#revoke {:format=>:json}
         oauth_introspect POST   /api/v1/oauth/introspect(.:format)         api/v1/doorkeeper/tokens#introspect {:format=>:json}
         oauth_token_info GET    /api/v1/oauth/token/info(.:format)         api/v1/doorkeeper/tokens#show {:format=>:json}

So, this is correct behavior of Rails. scope module and use_doorkeeper can be used if custom doorkeeper controller are required.

Was this page helpful?
0 / 5 - 0 ratings