Search code examples
ruby-on-railsapideviseruby-on-rails-5rails-admin

Rails Admin with Devise Admin user not working for API only app


I have an API only rails app, with Devise for user management and doorkeeper for token authorization from the Mobile App. This works fine.

I also installed Rails Admin, to manage all of the back end data in my DB and it works fine, I can work with Rails Admin from my browser and the mobile application access data through the API.

So far the Rails Admin dashboard is unprotected and I need to protect it so that only an administrator can modify the data from the DB.

I created another scope for devise with the tutorial from this link https://github.com/plataformatec/devise/wiki/How-to-Setup-Multiple-Devise-User-Models

but when I type the Rails Admin URL, the page is redirected to /admins/sign_in from devise, which I assume is the right behaviour, but then I get the following error:

Started GET "/" for 186.2.132.186 at 2019-06-11 15:14:40 +0000
   (0.9ms)  SET NAMES utf8,  @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'),  @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483
  ↳ /usr/local/rvm/gems/ruby-2.5.5/gems/activerecord-5.2.3/lib/active_record/log_subscriber.rb:98
   (0.9ms)  SELECT `schema_migrations`.`version` FROM `schema_migrations` ORDER BY `schema_migrations`.`version` ASC
  ↳ /usr/local/rvm/gems/ruby-2.5.5/gems/activerecord-5.2.3/lib/active_record/log_subscriber.rb:98
Processing by RailsAdmin::MainController#dashboard as HTML
Completed 401 Unauthorized in 10ms (ActiveRecord: 0.0ms)


Started GET "/admins/sign_in" for 186.2.132.186 at 2019-06-11 15:14:40 +0000
Processing by Admins::SessionsController#new as HTML
Completed 406 Not Acceptable in 16ms (ActiveRecord: 3.2ms)



ActionController::UnknownFormat (ActionController::UnknownFormat):

responders (2.4.1) lib/action_controller/respond_with.rb:213:in `respond_with'
devise (4.6.2) app/controllers/devise/sessions_controller.rb:14:in `new'

...more stack

On Rails ADmin initializer I added the following code:

  ## == Devise ==
  config.authenticate_with do
    warden.authenticate! scope: :admin
  end
  config.current_user_method(&:current_admin)

This are my routes:

#require 'api_constraints.rb'
Rails.application.routes.draw do

  use_doorkeeper do
   # No need to register client application
    skip_controllers :applications, :authorized_applications, :admins
  end

  #devise_for :admins, path: 'admins'
  devise_for :admins, controllers: {
          sessions: "admins/sessions",
          #registrations: 'admins/registrations',
      }, skip: [:registrations]

  mount RailsAdmin::Engine => '/', as: 'rails_admin'

  #devise_for :users
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
  scope module: :api, defaults: { format: :json }, path: 'api' do
    scope module: :v1 do
      devise_for :users, controllers: {
           registrations: 'api/v1/users/registrations',
       }, skip: [:sessions, :password]
    end
  end

   #Rutas para acceder al API
  #scope '/api' do
  namespace :api do
    namespace :v1 do
      resources :users
      resources :ejercicios
      resources :medidas
      resources :profiles
      resources :categoria_ejercicio
    end
  end

  root to: 'home#index'
end

I also included the Devise views for admin with this command

rails g devise:views admins

Can you please give me a hint or suggestion on how to make it work. Thanks.


Solution

  • The problem

    As you can see in the logs, the /admin/sign_in path is rejected with a 406 Not Acceptable http error, which precisely means the requested format (HTML) is not accepted.

    Started GET "/admins/sign_in" for 186.2.132.186 at 2019-06-11 15:14:40 +0000
    Processing by Admins::SessionsController#new as HTML
    Completed 406 Not Acceptable in 16ms (ActiveRecord: 3.2ms)
    

    This happens because you're using Rails in API mode, and in this mode Rails does not accept HTML requests anymore. Some middlewares & modules are disabled in order to improve performances for APIs.

    The problem is that Devise is requiring HTML because users are redirected to forms.

    Solution

    Another approach consists to use Basic authentication (Rails provide helpers for that). We get the credentials with basic auth (browser prompt), then we're using Devise only to authenticate the user. Finally we're configuration Rails Admin to use this strategy.

    # rails admin configuration, replacing your existing config
    config.authenticate_with do
      # this is a rails controller helper
      authenticate_or_request_with_http_basic('Login required') do |username, password|
    
        # Here we're checking for username & password provided with basic auth
        resource = Admin.find_by(email: username)
    
        # we're using devise helpers to verify password and sign in the user 
        if resource.valid_password?(password)
          sign_in :admin, resource
        end
      end
    end
    

    Note: you may have to adapt the resource = line with your models. I'm assuming you already have an Admin model.

    The code is not tested, but this is a start.

    Resources :