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.
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?
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.
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:
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.