I am integrating Devise Token Auth into my versioned Rails 5 API. Here is the structure:
Gemfile:
source 'https://rubygems.org'
...
gem 'omniauth', '~> 1.3'
gem 'devise_token_auth', '~> 0.1.38'
...
routes.rb:
require "api_constraints"
Rails.application.routes.draw do
namespace :api, defaults: { format: :json } do
scope module: :v1, constraints: ApiConstraints.new(version: 1, default: true) do
mount_devise_token_auth_for 'User', at: 'auth'
resources :users
end
end
end
application_controller.rb (app/controllers/api/v1/):
module Api
module V1
class ApplicationController < ActionController::API
include ActionController::Serialization
include DeviseTokenAuth::Concerns::SetUserByToken
end
end
end
When I try to POST:
{
"email": "testuser@domain.com",
"password": "testuserpassword"
}
to /api/auth/sign_in
, I get the error ActionController::RoutingError (uninitialized constant ApplicationController)
.
It seems that the devise_controller
doesn't have access to ApplicationController
when it tries to call its own methods. So I tried setting the base controller:
mount_devise_token_auth_for 'User', at: 'auth', base_controller: 'Api::V1::ApplicationController'
That also didn't work.
To fix the issue, I could remove the module
separation in application_controller.rb
to make it:
class ApplicationController < ActionController::API
include ActionController::Serialization
include DeviseTokenAuth::Concerns::SetUserByToken
end
but that of course causes a different load error (because things are no longer scoped correctly): LoadError (Unable to autoload constant Api::V1::ApplicationController, expected /home/ubuntu/workspace/app/controllers/api/v1/application_controller.rb to define it)
.
The only thing that actually works is to not have it scoped in a namespace/module at all:
Rails.application.routes.draw do
mount_devise_token_auth_for 'User', at: 'auth'
namespace :api, defaults: { format: :json } do
scope module: :v1,
constraints: ApiConstraints.new(version: 1, default: true) do
resources :users
end
end
end
but that defeats the whole purpose of versioning. Am I missing something?
I don't have that fancy default scoping happening, but this works for me:
Rails.application.routes.draw do
namespace :api do
scope :v1 do
mount_devise_token_auth_for 'User', at: 'auth'
end
end
end
A big difference is that in addition to my normal scoped-by-version ApplicationController, I have an ApplicationController at app/controller/application_controller.rb
that reads:
class ApplicationController < ActionController::API
include DeviseTokenAuth::Concerns::SetUserByToken
end
This may not be ideal, but it works with how DeviseTokenAuth is currently designed. A proper fix would likely involve a change inside of DeviseTokenAuth itself.
Hopefully this is helpful. With the above code, I have the following route that works to do what I need it to do:
api_user_session POST /api/v1/auth/sign_in(.:format) devise_token_auth/sessions#create
. . . and I don't get the 'different load error' you mentioned when you attempted a similar solution.