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.
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.
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.
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! 😉
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With