I am using devise_scope to define my routes, so as to shorten the list of generated routes that I do not use.
I have this routes file:
devise_for :users, class_name: 'Identity::User', skip: :all
namespace :users do
devise_scope :user do
post 'sign_in', controller: "/api/v1/identity/sessions", action: :create
delete 'sign_out', controller: "/api/v1/identity/sessions", action: :destroy
post '/', controller: "/api/v1/identity/registrations", action: :create
end
I have confirmed this generates the routes as expected. When the sign_in route is hit, it does appear to hit the proper controller, SessionController, which extends Devise::SessionsController. However, I get the below error:
Could not find devise mapping for path "/api/v1/users/sign_in".
This may happen for two reasons:
1) You forgot to wrap your route inside the scope block. For example:
devise_scope :user do
get "/some/route" => "some_devise_controller"
end
2) You are testing a Devise controller bypassing the router.
If so, you can explicitly tell Devise which mapping to use:
#request.env["devise.mapping"] = Devise.mappings[:user]
Completed 404 Not Found in 1ms (ActiveRecord: 0.0ms)
I assume that somehow the devise_for is not properly setting up the routes I need?
EDIT: Solution, but not ideal.
The issue in my case, was the devise_for was not mapping properly. The full routes included another namespace that I neglected to paste:
namespace :api do
devise_for :users, class_name: 'Identity::User', skip: :all
namespace :users do
devise_scope :user do
post 'sign_in', controller: "/api/v1/identity/sessions", action: :create
delete 'sign_out', controller: "/api/v1/identity/sessions", action: :destroy
post '/', controller: "/api/v1/identity/registrations", action: :create
end
end
end
However, if I move devise for outside of the api namespace, the mappings are correct:
devise_for :users, class_name: 'Identity::User', skip: :all
namespace :api do
namespace :users do
devise_scope :user do
post 'sign_in', controller: "/api/v1/identity/sessions", action: :create
delete 'sign_out', controller: "/api/v1/identity/sessions", action: :destroy
post '/', controller: "/api/v1/identity/registrations", action: :create
end
end
end
I use nested routes files, so doing this breaks my structure. Is there a way to adjust this to work with it inside of that api namespace?
so basically I believe that devies is trying to use the "devise" controllers while being namespaced.
So when inside the :api namespace rails looks for Api::Devise::Sessions (or something similar) controller while devise expects just Devise::Sessions
what to do in this case?
try devise_for :users, class_name: 'Identity::User', path: :api, skip: :all
you can try using scope :api do instead of namespace :api do
this affects the created paths but rails still should be looking for correct devise controller
explicitly tell devise which controller to use
and idea of how exactly point 3 can look like:
namespace :api do
devise_for :users, class_name: 'Identity::User', controllers: { session: "api/v1/sessions" }, skip: :all
namespace :users do
devise_scope :user do
post 'sign_in', controller: "/api/v1/identity/sessions", action: :create
delete 'sign_out', controller: "/api/v1/identity/sessions", action: :destroy
post '/', controller: "/api/v1/identity/registrations", action: :create
end
end
end
Related
I'm overriding Devise controllers in Rails like that:
module Api
module V1
module Devise
class RegistrationsController < Devise::RegistrationsController
....
end
end
end
end
And thats my routes:
Rails.application.routes.draw do
root 'application#index'
devise_for :users
devise_for :taxi_drivers, only: :passwords
resources :taxi_drivers_groups, only: %i[index new create edit update]
namespace :api do
namespace :v1 do
devise_for :users,
defaults: { format: :json },
class_name: 'User',
skip: %i[registrations sessions passwords],
path: '',
path_names: { sign_in: 'login', sign_out: 'logout' }
devise_scope :user do
post 'signup', to: 'devise/registrations#create'
post 'login', to:'devise/sessions#create'
delete 'logout', to: 'devise/sessions#destroy'
post 'password_recover', to: 'devise/passwords#create'
put 'password_recover', to: 'devise/passwords#update'
end
end
end
end
And I'm getting an error when I try to pass my tests:
ActionController::RoutingError: uninitialized constant Api::V1::Devise::RegistrationsController
And in my test file:
test 'user invalid sigup with empty fields' do
#valid_signup_params[:name] = nil
post('/api/v1/signup.json', { user: #valid_signup_params }, headers: { format: :json })
assert_equal last_response.status, 422
assert_equal json_response['errors']['name'].count, 1
end
Do you have any idea how to fix this error?
Thanks!
The issues comes from a constant clash between the newly defined controller and the existing one.
Replacing class RegistrationsController < Devise::RegistrationsController with class RegistrationsController < ::Devise::RegistrationsController is fixing the issue as ruby knows that he has to find the RegistrationsController class in the previously defined Devise module and not in the current definition.
I have a user model which has admin and manager role column (using devise), need to have different sign in page for admins and manager
devise_for :users, controllers: {
sessions: 'users/sessions'
}
as :user do
namespace :admins do
get 'sign_in', to: 'sessions#new'
post 'sign_in', to: 'sessions#create'
end
end
for admin role it has access the Admins::SessionsController controller and for manager it has to access Users::SessionsController, how can I specify that in routes without using devise_for ?
You need different scopes for user and admin.
devise_for :users, skip: :all
devise_scope :user do
# custom routes here
end
devise_scope :admin do
# custom routes here
end
Note that the resource is plural for devise_for but singular for devise_scope.
I have put login and signup in one page and every thing works fine except when I encounter errors. Then the page redirects to their default pages and show errors there. In my case the login redirects me to the default domain.com/users/sign_in , but signup redirects me to domain.com/users.
routes.rb
Rails.application.routes.draw do
root 'visitor#index'
namespace :admin do
# get "/stats" => "stats#stats"
devise_scope :admin_user do
get '/stats/:scope' => 'stats#stats', as: :admin_stats
end
end
devise_for :admin_users, ActiveAdmin::Devise.config
ActiveAdmin.routes(self)
namespace :client do
get 'dashboard' => 'dashboard#index', as: 'dashboard'
# resources :verification, only: [:create, :index, :destroy]
get 'verification' => 'verification#index', as: 'verification'
match 'verification' => 'verification#upload', as: 'verification_upload', via: [:post, :patch]
end
devise_for :users, class_name: 'FormUser', controllers: { omniauth_callbacks: 'omniauth_callbacks', registrations: 'registrations' }
# devise_scope :user do
# root to: 'devise/registrations#new'
# end
end
you can use a CustomFailure class to control where the redirect goes if Devise fails to authenticate.
It's explained at this wiki page...
https://github.com/plataformatec/devise/wiki/How-To:-Redirect-to-a-specific-page-when-the-user-can-not-be-authenticated
I'm trying to split my rails project in a front-end for regular users and a back-end for admins. Therefore i have created a namespace 'admin' so that i can easily control admin.After creating the admin namespace the I changed the routes from
Rails.application.routes.draw do
authenticated :user do
root to: 'dashboard#index', as: :authenticated_root
end
unauthenticated do
root to: "home#index"
end
match '(errors)/:status', to: 'errors#show', constraints: { status: /\d{3}/ }, via: :all
devise_for :users, skip: [:registrations]
as :user do
get 'my/profile/edit' => 'devise/registrations#edit', as: 'edit_user_registration'
patch 'my/profile' => 'devise/registrations#update', as: 'user_registration'
end
resources :users
resources :events do
patch :archive, :unarchive
end
end
to this
Rails.application.routes.draw do
namespace :admin do
authenticated :user do
root to: 'dashboard#index', as: :authenticated_root
end
unauthenticated do
root to: "home#index"
end
match '(errors)/:status', to: 'errors#show', constraints: { status: /\d{3}/ }, via: :all
devise_for :users, skip: [:registrations]
as :user do
get 'my/profile/edit' => 'devise/registrations#edit', as: 'edit_user_registration'
patch 'my/profile' => 'devise/registrations#update', as: 'user_registration'
end
resources :users
resources :events do
patch :archive, :unarchive
end
end
end
After these change I got this page
Rails::WelcomeController#index as HTML
Does anyone know how to do this?
If I understand what you ask, you want to put everything admin related into the admin namespace, but leave everything (for example, the root page) outside.
But in your routing example, you put everything inside the admin namespace, even the root page.
So generally, you want something like:
Rails.application.routes.draw do
namespace :admin do
# put admin stuff here
end
# put everything NOT in the admin interface outside your namespace
# you want a root route here. That's the page that'll be displayed by default
root to :your_root_stuff
# and if you have users who aren't admins, devise and authenticated routes too
# ... other stuff
end
I am using Devise 3.1.1 and am trying to redirect user to the Sign In page after he signs up.
As instructed in Devise's wiki I overridden RegistrationsController with the following:
class RegistrationsController < Devise::RegistrationsController
protected
def after_inactive_sign_up_path_for(resource)
'/users/sign_in'
end
end
As instructed, I also added the following line to the routes.rb:
devise_for :users, controllers: { registrations: 'registrations'}
After which I get the following error when I go to sign in page:
Invalid route name, already in use: 'new_user_session' You may have defined two routes with the same name using the:asoption, or you may be overriding a route already defined by a resource with the same naming.
In my routes I already have this defined:
devise_for :users, skip: :registrations
devise_scope :user do
resource :registration,
# disabled :edit & :destroy
only: [:new, :create, :update],
path: 'users',
path_names: { new: 'sign_up' },
controller: 'devise/registrations',
as: :user_registration do
get :cancel
end
end
You can only define the devise_for block once, and as you're already messing with the default registrations controller you should be able to just do something like the following to have devise use your controller:
devise_for :users, skip: :registrations
devise_scope :user do
resource :registration,
# disabled :edit & :destroy
only: [:new, :create, :update],
path: 'users',
path_names: { new: 'sign_up' },
controller: 'registrations',
as: :user_registration do
get :cancel
end
end