Devise Token Auth - versioned API: uninitialized constant ApplicationController

devise auth
rails token authentication
devise token auth omniauth
devise token auth jwt
mount_devise_token_auth_for

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.

Cannot use this gem alongside Devise · Issue #192 , Devise Token Auth - versioned API: uninitialized constant ApplicationController class ApplicationController < ActionController::API include  I am using Razorpay webhook to update which user paid for the sucscription in the controller. I am able to receive the Webhook payload in controller which inherits from ActionController::API inst

Devise tries to make its built-in controllers inherit from your own ApplicationController, but in your app that class doesn't exist. Instead you have an Api::V1::ApplicationController class.

Fortunately you can configure Devise to inherit from something else. In your config/initializers/devise.rb, do this:

config.parent_controller = 'Api::V1::ApplicationController'

devise_token_auth/CHANGELOG.md at master · lynndylanhurley , 20150318160543 DeviseTokenAuthCreateUsers: migrating module Api class BaseController < ApplicationController include uninitialized constant ActionDispatch::Routing::Mapper::Scope Using rails -> 4.8.1 and the latest version of device_token_auth, I've also this error when running rake routes : When you run the devise_token_auth installation, it is supposed to automatically create this concern in the ApplicationController. This concern gives access to the helper methods like authenticate_user .

Just in case someone lands here with the same error. The answer from @Ecnalyr helped me a lot but was not enough because sometimes my request would call only controllers/api/v1/application_controller.rb and sometimes only controllers/application_controller.rb (and for other specific reasons related to my particular needs).

So here is what I did :

# controllers/base_controller.rb
class BaseController < ActionController::API
  include DeviseTokenAuth::Concerns::SetUserByToken
  include ActionController::Serialization

  before_action :set_global_headers

  private

  def set_global_headers
    response.headers["Content-Type"] = "application/json"
    response.headers["X-Robots-Tag"] = "none"
  end
end

So BaseController is my crossing controller for global configuration.

# controllers/api/v1/application_controller.rb
module Api
  module V1
    class ApplicationController < BaseController
      include Api::V1::ResponseHelper # app/helpers/api/v1/response_helper.rb

      before_action :authenticate_api_v1_user!, except: [:new, :create]
      before_action :set_v1_headers

      private

      def set_v1_headers
        # Custom header specific to v1
      end
    end
  end
end

I then added config.parent_controller = 'BaseController' to the devise.rb in initializers.

My routes looks like this :

namespace :api do
  namespace :v1 do
    mount_devise_token_auth_for 'User', as: 'v1', at: 'auth', controllers: {
      token_validations:  'api/v1/users/token_validations',
      registrations:      'api/v1/users/registrations',
      passwords:          'api/v1/users/passwords',
      sessions:           'api/v1/users/sessions'
    }
  end
end

I added the Devise user controllers with my controllers just in case I have to edit them.

# controllers/api/v1/users/registrations_controller.rb
module Api
  module V1
    module Users
      class RegistrationsController < DeviseTokenAuth::RegistrationsController
        def create
          puts params # For debug
          super
          # Some action after registration
        end
      end
    end
  end
end

I think that's all.

How to Install Rails 5 API - Code The World, Is uid mandatory for devise token auth to find current user? #932; token authentication not working on production #931; Getting 'uninitialized constant undefined method `authenticate_user!' when want to version my api #908 multiple congratulation emails #774; Set up a new API application controller not working? getting ng-token-auth and devise_token_auth to work with OAuth in ionic InAppBrowser #367 omniauth callback redirect not working properly when using namespace/scope #362 invalid token in method set_user_by_token on RegistrationsController#update #357

Token-based authentication with Ruby on Rails 5 API, Token based authentication with the devise_token_auth gem. A namespaced API Using rails-api, tell devise to not use ActionDispatch::Flash # middleware Mime-Version: 1.0 class ApiController < ApplicationController If we don't do this RSpec Cannot find the Controllers and throws an Uninitialized Constant error​. I am working on a Rails 5 API only app using JWT tokens.. I'd like to use Rails Admin for internal processes and would like to configure Devise (or Clearance) for auth for staff members instead of integrating JWT tokens with Rails Admin directly.

Building the Perfect Rails API, Token-based authentication (also known as JSON Web Token authentication) An API application is a trimmed-down version of standard Rails application to all controllers, it has to be declared in the ApplicationController :. gem 'devise_token_auth' gem 'omniauth' Run bundle install to install the gems. Generating the user model with devise concerns and routes. rails g devise_token_auth:install User auth. The devise generator accepts two arguments the user class (in our case User) which is the name of the class to use for user authentication.

Discover what's new in the latest version of the Ruby , Before the advent of Rails 5, the best option for building APIs in Ruby was arguably Grape. gem install rails # version should be >= Rails 5.2.0 rails --​version class ApplicationController < ActionController::API include A good way of authenticating API requests is using HTTP token based authentication, which involves  Using those params properly, we can kindly keep user login at different devices. How the authentication works. As shown above, access-token is used as 'password' for each request.

Comments
  • Thanks for your answer! I'm at work right now so I can't test this, but I will in a couple of hours.
  • This did the trick, although I feel like I had already tried that... I guess not! In case that you actually have multiple versions to your API, I needed to make the scope: scope :v1, as: :v1 in order to not have the error: Invalid route name, already in use: 'new_api_user_session'. The versions could also be namespaced instead of scoped, which would solve that issue, but that seems to leave the overall structure less flexible.
  • Thanks for your answer Paul, but that defeats the purpose of versioning, since only one API version could have the correct parent controller.