1
votes

I have a Rails application with an API inside. I can make calls and authenticate with the access-token from the gem devise_auth.

I wanted to add now authentication for web requests. I decided to create two application_controllers to handle the two types of request, web and api.

The two controllers look like this:

class ApplicationController < ActionController::Base
  before_action :authenticate_user!
  protect_from_forgery with: :exception

end

And for api:

class ApiApplicationController < ActionController::Base
  skip_before_action :verify_authenticity_token
  include DeviseTokenAuth::Concerns::SetUserByToken
end

The routes look like this:

Rails.application.routes.draw do
  devise_for :users, as: 'web'

  mount_devise_token_auth_for 'User', at: 'auth'
  root to: 'pages#home'
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
  namespace :api, defaults: { format: :json } do
    namespace :v1 do
      resources :trainings, only: [ :index, :show, :create ]
      post 'import', to: "trainings#import"
    end
  end

  namespace :admin do
    resources :trainings, only: [:index]
    resources :users, only: [:index, :show]
  end
end

I just added the web prefix to avoid collision with same rails_path.

The controller that I want to access is the admin/users, and it inherites from ApplicationController:

class Admin::UsersController < ApplicationController
  before_action :authenticate_user!

When I type in the browser:

http://localhost:3000/admin/users

I expect to see the normal devise behaviour, which is, seeing the login form in the web.

Unfortunately, the request is received by the AuthTokenController:

Started GET "/auth/sign_in" for ::1 at 2019-07-23 15:37:46 +0200
Processing by DeviseTokenAuth::SessionsController#new as HTML
Filter chain halted as :authenticate_user! rendered or redirected
Completed 401 Unauthorized in 1ms (Views: 0.3ms | ActiveRecord: 0.0ms)

I split up the controller for the web and for api, so that I can play with the protect_from_forgery method etc.

But I don't know how to tell Rails in the Routes, that whenever I do a web request, that it authenticates through the right controller.

To sum up, I am hitting following url:

http://localhost:3000/admin/users

Which corresponds to this controller:

admin_users GET      /admin/users(.:format)  admin/users#index

This controller inherits from

class Admin::UsersController < ApplicationController
  before_action :authenticate_user!

And this ApplicationController uses:

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  before_action :authenticate_user!
end

At no moment I am telling to this request anything about the DeviseAuthToken. I don't know why it takes the request.

My User model is as follows:

# frozen_string_literal: true

class User < ActiveRecord::Base
  has_many :trainings

  extend Devise::Models
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable
  include DeviseTokenAuth::Concerns::User
end
1
Were you able to resolve this? I am running into the exact same issue. - Romuloux

1 Answers

1
votes

From the Devise documentation:

For Rails 5, note that protect_from_forgery is no longer prepended to the before_action chain, so if you have set authenticate_user before protect_from_forgery, your request will result in "Can't verify CSRF token authenticity." To resolve this, either change the order in which you call them, or use protect_from_forgery prepend: true.