Skip to content

Instantly share code, notes, and snippets.

@Genkilabs
Created June 29, 2017 17:40
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Genkilabs/587fab4214798c87c3aa97bb06280f43 to your computer and use it in GitHub Desktop.
Save Genkilabs/587fab4214798c87c3aa97bb06280f43 to your computer and use it in GitHub Desktop.
How to use Devise inside of a Versionist scope without the version-name in the model's path prefix
#/config/initializers/inflections.rb
# This is a nice-to-have so API doesn't keep showing up as Api
ActiveSupport::Inflector.inflections(:en) do |inflect|
inflect.acronym 'API'
end
#/config/routes.rb
api_version(:module => "API::V1",:path => {:value => "api/v1"}) do
devise_for :users,
:as => :fancy, #removes "api_v1" from the derived model scope, but it cant be blank so pick something that doesn't contain the version.
:path => "", #removes "users" from the path so it is just "api/:ver/login"
# Here is where we add a custom, versioned, controller if we want one. It is not necessary though.
# :controllers => { :sessions => "api/v1/sessions" }, #routes our session to our custom controller
:path_names => { sign_in: 'login', sign_out: 'logout'} #changes the 'action' part of the path name, just flavour.
end
#if you need a scope, you must now include the scope you choose in devise_for :as. for example:
devise_scope :fancy_user do
#helper route so we can test with a logout link or url using GET
get :logout, :controller => :sessions, :action => :destroy
end
end
@Genkilabs
Copy link
Author

Genkilabs commented Jun 29, 2017

The need for this arose while combining the excellent tutorial by @trabe on remote authentication in Devise (http://4trabes.com/2012/10/31/remote-authentication-with-devise/), with the need to version this authentication using Versionist. Basically, the problem is that when you nest devise inside the api_version block provided by Versionist, then the path, in this case "api/v1" is added to the actual object scope sent to Warden. So you are no longer authenticating a :user but an :api_v1_user.
What does that really mean? It means that devise methods like current_user become current_api_v1_user! Of course that is ugly, but it's really going to be a problem as soon as v2 comes out ;)

Just remember that since you changed the scope, if you want to do some custom strategy then you would need to reference it that way when you register it with warden. ie. initializers/devise.rb ends up like:

config.warden do |manager|
  manager.strategies.add(:remote_authenticatable, Devise::Strategies::RemoteAuthenticatable)
  manager.default_strategies(:scope => :fancy_user).unshift :remote_authenticatable
end
config.add_module :remote_authenticatable, :controller => :sessions, :route => { :session => :routes }

Hope this helps someone, enjoy ^_^

@darr1s
Copy link

darr1s commented Aug 20, 2017

Hi there, thanks for providing the snippet. However, I am getting an uninitialized constant issue, have you encounter this issue before?

I did not create any custom controllers for devise. My own controllers work fine living under /api/v1.

Thanks in advance.

config/routes.rb

  
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
  
  mount Rswag::Ui::Engine => '/api-docs'
  mount Rswag::Api::Engine => '/api-docs'
  
  # API version 1
  api_version(:module => "API::V1", :path => {:value => "api/v1"}) do
    
    resources :clubs
    resources :events
    resource :galleries do
      resources :images, :only => [:create, :destroy]
    end
    
    devise_for  :users,
                :as => :default,
                :path => '',
                :path_names => {
                  sign_in: 'login',
                  sign_up: 'register',
                  sign_out: 'logout'
                }
                
  end
  
  devise_scope :default_user do
    get :logout, :controller => :sessions, :action => :destroy
  end
  
end

** $ rails routes **

➜ rails routes
                          Prefix Verb   URI Pattern                            Controller#Action
                        rswag_ui        /api-docs                              Rswag::Ui::Engine
                       rswag_api        /api-docs                              Rswag::Api::Engine
                    api_v1_clubs GET    /api/v1/clubs(.:format)                api/v1/clubs#index
                                 POST   /api/v1/clubs(.:format)                api/v1/clubs#create
                     api_v1_club GET    /api/v1/clubs/:id(.:format)            api/v1/clubs#show
                                 PATCH  /api/v1/clubs/:id(.:format)            api/v1/clubs#update
                                 PUT    /api/v1/clubs/:id(.:format)            api/v1/clubs#update
                                 DELETE /api/v1/clubs/:id(.:format)            api/v1/clubs#destroy
                   api_v1_events GET    /api/v1/events(.:format)               api/v1/events#index
                                 POST   /api/v1/events(.:format)               api/v1/events#create
                    api_v1_event GET    /api/v1/events/:id(.:format)           api/v1/events#show
                                 PATCH  /api/v1/events/:id(.:format)           api/v1/events#update
                                 PUT    /api/v1/events/:id(.:format)           api/v1/events#update
                                 DELETE /api/v1/events/:id(.:format)           api/v1/events#destroy
         api_v1_galleries_images POST   /api/v1/galleries/images(.:format)     api/v1/images#create
          api_v1_galleries_image DELETE /api/v1/galleries/images/:id(.:format) api/v1/images#destroy
                api_v1_galleries GET    /api/v1/galleries(.:format)            api/v1/galleries#show
                                 PATCH  /api/v1/galleries(.:format)            api/v1/galleries#update
                                 PUT    /api/v1/galleries(.:format)            api/v1/galleries#update
                                 DELETE /api/v1/galleries(.:format)            api/v1/galleries#destroy
                                 POST   /api/v1/galleries(.:format)            api/v1/galleries#create
        new_default_user_session GET    /api/v1/login(.:format)                api/v1/sessions#new
            default_user_session POST   /api/v1/login(.:format)                api/v1/sessions#create
    destroy_default_user_session DELETE /api/v1/logout(.:format)               api/v1/sessions#destroy
       new_default_user_password GET    /api/v1/password/new(.:format)         api/v1/passwords#new
      edit_default_user_password GET    /api/v1/password/edit(.:format)        api/v1/passwords#edit
           default_user_password PATCH  /api/v1/password(.:format)             api/v1/passwords#update
                                 PUT    /api/v1/password(.:format)             api/v1/passwords#update
                                 POST   /api/v1/password(.:format)             api/v1/passwords#create
cancel_default_user_registration GET    /api/v1/cancel(.:format)               api/v1/registrations#cancel
   new_default_user_registration GET    /api/v1/register(.:format)             api/v1/registrations#new
  edit_default_user_registration GET    /api/v1/edit(.:format)                 api/v1/registrations#edit
       default_user_registration PATCH  /api/v1(.:format)                      api/v1/registrations#update
                                 PUT    /api/v1(.:format)                      api/v1/registrations#update
                                 DELETE /api/v1(.:format)                      api/v1/registrations#destroy
                                 POST   /api/v1(.:format)                      api/v1/registrations#create
                          logout GET    /logout(.:format)                      sessions#destroy

Routes for Rswag::Ui::Engine:
  root GET  /           rswag/ui/home#index

Routes for Rswag::Api::Engine:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment