15

I am having a hard time getting my head around the responsibilities and capabilities of the popular Doorkeeper and Devise gems. I am not overly experienced in authorization and authentication so pardon me if I misunderstood certain aspects of those areas. I do try hard and if I do something I want to do it the right way, so here is my current situation:

I want to build an API-only rails application that is responsible for authenticating and authorizing users as they sign up and use the service. I handpicked two fairly popular gems called Doorkeeper (authorization) and Devise (authentication).

I currently have this structure in place and it works, however, I'm having issues fully getting behind what the responsibilities of these gems are. So as far as I understand, the Devise gem serves as an authentication layer, meaning that the user can be identified and logged in (additional features will be discussed below). Doorkeeper on the other hand will ensure that resources can only be accessed by members who are authorized to do so. I have chosen Doorkeeper for OAuth2 integration because my server needs to be able to give access to the API to potential third parties in the future.

My question first and foremost is whether my assumptions about those gems is correct.

Here is the current authentication/authorization flow:

Issue: User signs up, how do I leverage Devise to send a confirmation email if my API is devoid of the preconfigured views provided by Devise? (Side Note: The traits Recoverable, Rememberable, Trackable, and Confirmable are in the User model/migration.)

Similarly, I would love to know how to implement a potential password reset. Notice that references to examples would suffice too as long as they are applicable to my use case.

I know that Devise offers these capabilities, but it's hard to make out how to do it without hitting their preconfigured (view?) routes.

For example, when a user signs up, he hits my own user_controller's create method, which basically just creates a new user, is that supposed to automatically send a confirmation email (if we assume that my mail config is correct)?

I am not entirely sure whether avoiding the preconfigured routes makes a lot of sense, that's why I'd like to hear from more experienced people who may have used those gems in the past if my thinking is correct or whether I'm completely off on this.

Jai Chauhan
  • 4,035
  • 3
  • 36
  • 62
the_critic
  • 12,720
  • 19
  • 67
  • 115
  • https://github.com/doorkeeper-gem/doorkeeper/wiki/example-applications – p4sh4 Jan 27 '16 at 03:40
  • Sorcery with JWT is an option? Look this example: http://tangosource.com/blog/rails-basic-api-authentication-with-sorcery-and-jwt/ . – Alvaro Inckot Apr 25 '18 at 11:24
  • 1
    It's been more than 4 years. You probably have all the answers and more. This is exactly what I'm trying to do. Can you post what you ended up doing and if I need to do something differently since we're in distant future? – adominey Jan 20 '21 at 11:28

2 Answers2

1

I've done exactly what you're looking for and I really think this is a good choice. I've never regret it. What you're actually trying to do is to have the exact same behaviour that native Devise implementation but on an Engine via API. Plus, you want to apply it to a Doorkeeper authorisation.

Short answer is: override default views/controller/routes.

Why should I override views?

You're using Rails API means response will be JSON but you're using a certain standard to wrap it and Devise might not be the same. For example, if you want to respect OpenAPI standards, it's not possible natively (please correct me if I'm wrong).

This leads to an override of the controller as well to be 100% sur of what you're returning. I would also add an extra layer here about versioning. Now you're plans might be fixed but you don't know about tomorrow.

Overriding controller leads to an override of the routes itselves.

Wait, how am I suppose to do that?

On your global Gemfile.rb

# Gemfile.rb
gem 'devise'

Nothing changes for devise initializers if it's on an engine or not Keep config/initializers/devise.rb as usual

Routes are now defined on your engine:

# your_engine/config/routes.rb

YourEngine::Engine.routes.draw do
  namespace :v1, format: false, defaults: { format: :json } do
    devise_for :users, controllers: { sessions: 'your_engine/v1/users/sessions',
                                      registrations: 'your_engine/v1/users/registrations',
                                      confirmations: 'your_engine/v1/users/confirmations',
                                      unlocks: 'your_engine/v1/users/unlocks',
                                      omniauth_callbacks: 'your_engine/v1/users/omniauth_callbacks',
                                      passwords: 'your_engine/v1/users/passwords' },
                       skip: %i[sessions]
  end
end

I've deliberately skipped sessions since we're talking about API and there is no such things (for now).

Add Devise to Doorkeeper using resource owner password credentials:

Doorkeeper.configure do
    base_controller 'ActionController::API'

    api_only

    resource_owner_from_credentials do |routes|
        user = User.find_for_database_authentication(:email => params[:email])
        if user && (user.valid_for_authentication? { user.valid_password?(params[:password]) })
          user
        end
    end
end

All your controllers must inherit from Device controllers. I inspired myself from it to made my overrides.

This way, you can have your API entrypoint for Devise but you can also have another one with a non-api access building another Engine. It's the same process but requirements come from a different order that for the API (views -> controllers -> routes). For Devise on an engine, I would say order of comprehension is routes -> controllers -> views.

I hope this helps you. Good luck!

brcebn
  • 1,571
  • 1
  • 23
  • 46
-1

Devise is really made to do everything for you, if you bypass the automatic mecanisms and want to re-implement them by hand... you WILL make a mistake. Don't bypass controllers mecanism and use the devise's wiki for small customizations like changing the redirection after creation/update.

Devise handles confirmation emails (confirmable) and password reset (recoverable) system for you so use their system, it is just options to activate.

If you wanna change their view (we always want to customize the layout) you can :

rails g devise:views

It will keep the devise flow, with your custom views.

If you wanna stop using a specific mecanism, just remove it from the model.

devise :database_authenticatable, :registerable, :confirmable, :recoverable

Avoid preconfigured routes would have made sense if did not used them at all (meaning removing the option and using except in routes), but as you want all the mecanisms Devise provides it does not make sense here.

Their Wiki is really dense but read it again and again, everything you need is here.

Finally : Yes Gatekeeper provides a good OAuth2 provider system and mixes up very well with Devise, so you made a good choice here. Their views system is also customizable.

You might also want to take a look at cancancan that handle roles and rights for your users (simple, API, admin, etc).

Hope it helps !

gdurelle
  • 2,079
  • 22
  • 38
  • 1
    I know this answer is a couple of years old and the question even older, but I came to SO with exactly the same question as the original poster and this answer doesn't really suffice. When you're dealing with an API-only Rails app, managing password flows requires something other than Devise's built-in views (or adapted versions thereof). – ScottM Apr 27 '21 at 13:59
  • I don't think this had answered the original question. It just restated what Devise and Doorkeeper do without clarifying the ways to properly integrate them into Rails API. – Ray Jasson Feb 22 '22 at 14:18