How do I remove the Devise route to sign up?

Ruby on-Rails-3RoutingDevise

Ruby on-Rails-3 Problem Overview


I'm using Devise in a Rails 3 app, but in this case, a user must be created by an existing user, who determines what permissions he/she will have.

Because of this, I want:

  • To remove the route for users to sign up.
  • To still allow users to edit their profiles (change email address and password) after they have signed up

How can I do this?

Currently, I'm effectively removing this route by placing the following before devise_for :users:

match 'users/sign_up' => redirect('/404.html')

That works, but I imagine there's a better way, right?

Update

As Benoit Garret said, the best solution in my case is to skip creating the registrations routes en masse and just create the ones I actually want.

To do that, I first ran rake routes, then used the output to re-create the ones I wanted. The end result was this:

devise_for :users, :skip => [:registrations] 
as :user do
  get 'users/edit' => 'devise/registrations#edit', :as => 'edit_user_registration'
  put 'users' => 'devise/registrations#update', :as => 'user_registration'
end

Note that:

  • I still have :registerable in my User model
  • devise/registrations handles updating email and password
  • Updating other user attributes - permissions, etc - is handled by a different controller

Actual answer:

Remove the route for the default Devise paths; i.e.:

devise_for :users, path_names: {
  sign_up: ''
}

Ruby on-Rails-3 Solutions


Solution 1 - Ruby on-Rails-3

you can do this in your model

# typical devise setup in User.rb
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable

change it to:

devise :database_authenticatable, :recoverable, :rememberable, :trackable, :validatable

notice that the symbol :registerable was removed

That's it, nothing else is required. All routes and links to registration page are magically removed too.

Solution 2 - Ruby on-Rails-3

I tried to do this as well, but a thread on the devise google group dissuaded me from searching for a really clean solution.

I'll quote José Valim (the Devise maintainer) :

> There isn't a straight-forward option. You can either provide a patch > or use :skip => :registerable and add only the routes you want.

The original question was :

> Is there any good way to remove a specific route (the delete route) > from Rails?

Solution 3 - Ruby on-Rails-3

I had similar issue tried to remove devise_invitable paths for create and new :

###before:

 devise_for :users

rake routes

accept_user_invitation GET    /users/invitation/accept(.:format)           devise/invitations#edit
       user_invitation POST   /users/invitation(.:format)                  devise/invitations#create
   new_user_invitation GET    /users/invitation/new(.:format)              devise/invitations#new
                       PUT    /users/invitation(.:format)                  devise/invitations#update

###after

devise_for :users , :skip => 'invitation'
devise_scope :user do
  get "/users/invitation/accept", :to => "devise/invitations#edit",   :as => 'accept_user_invitation'
  put "/users/invitation",        :to => "devise/invitations#update", :as => nil
end

rake routes

accept_user_invitation GET    /users/invitation/accept(.:format)                 devise/invitations#edit
                       PUT    /users/invitation(.:format)                        devise/invitations#update

note 1 devise scope https://github.com/plataformatec/devise#configuring-routes

note 2 I'm applying it on devise_invitable but it will work with any devise *able feature

Important note: see that devise_scope is on user not users ? that's correct, watch out for this ! It can cause lot of pain giving you this problem:

Started GET "/users/invitation/accept?invitation_token=xxxxxxx" for 127.0.0.1 
Processing by Devise::InvitationsController#edit as HTML
  Parameters: {"invitation_token"=>"6Fy5CgFHtjWfjsCyr3hG"}
 [Devise] Could not find devise mapping for path "/users/invitation/accept?  invitation_token=6Fy5CgFHtjWfjsCyr3hG".
This may happen for two reasons:

1) You forgot to wrap your route inside the scope block. For example:

  devise_scope :user do
     match "/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]

Solution 4 - Ruby on-Rails-3

I found another post similar to this one and wanted to share an answer @chrisnicola gave. In the post they were attempting to only block user signup's during production.

You could also modify the registrations controller. You can use something like this:

In "app/controllers/registrations_controller.rb"

class RegistrationsController < Devise::RegistrationsController
  def new
    flash[:info] = 'Registrations are not open.'
    redirect_to root_path
  end

  def create
    flash[:info] = 'Registrations are not open.'
    redirect_to root_path
  end
end

This will override devise's controller and use the above methods instead. They added flash messages incase that someone somehow made it to the sign_up page. You should also be able to change the redirect to any path you like.

Also in "config/routes.rb" you can add this:

devise_for :users, :controllers => { :registrations => "registrations" }

Leaving it like this will allow you to use the standard devise edit your profile. If you wish you can still override the edit profile option by including

  def update
  end

in the "app/controllers/registrations_controller.rb"

Solution 5 - Ruby on-Rails-3

You can override the "devise_scope" by placing it before the "devise_for".

devise_scope :user do
  get "/users/sign_up",  :to => "sites#index"
end

devise_for :users

Not sure if this is the best way but its my solution currently, as it just redirects back to the sign in page.

Solution 6 - Ruby on-Rails-3

This is an old question - but I recently had solve the same issue and came up with a solution which is far more elegant than:

devise_for :users, :skip => [:registrations] 
as :user do
  get 'users/edit' => 'devise/registrations#edit', :as => 'edit_user_registration'
  put 'users' => 'devise/registrations#update', :as => 'user_registration'
end

And it gives the default names for the named routes (like cancel_user_registration) without being excessively verbose.

devise_for :users, skip: [:registrations]

# Recreates the Devise registrations routes
# They act on a singular user (the signed in user)
# Add the actions you want in 'only:'
resource :users,
    only: [:edit, :update, :destroy],
    controller: 'devise/registrations',
    as: :user_registration do
  get 'cancel'
end

rake routes output with the default devise modules:

                  Prefix Verb   URI Pattern                    Controller#Action
        new_user_session GET    /users/sign_in(.:format)       devise/sessions#new
            user_session POST   /users/sign_in(.:format)       devise/sessions#create
    destroy_user_session DELETE /users/sign_out(.:format)      devise/sessions#destroy
           user_password POST   /users/password(.:format)      devise/passwords#create
       new_user_password GET    /users/password/new(.:format)  devise/passwords#new
      edit_user_password GET    /users/password/edit(.:format) devise/passwords#edit
                         PATCH  /users/password(.:format)      devise/passwords#update
                         PUT    /users/password(.:format)      devise/passwords#update
cancel_user_registration GET    /users/cancel(.:format)        devise/registrations#cancel
  edit_user_registration GET    /users/edit(.:format)          devise/registrations#edit
       user_registration PATCH  /users(.:format)               devise/registrations#update
                         PUT    /users(.:format)               devise/registrations#update
                         DELETE /users(.:format)               devise/registrations#destroy

Solution 7 - Ruby on-Rails-3

I liked @max's answer, but when trying to use it I ran into an error due to devise_mapping being nil.

I modified his solution slightly to one that seems to address the issue. It required wrapping the call to resource inside devise_scope.

devise_for :users, skip: [:registrations]

devise_scope :user do
  resource :users,
           only: [:edit, :update, :destroy],
           controller: 'devise/registrations',
           as: :user_registration do
    get 'cancel'
  end
end

Note that devise_scope expects the singular :user whereas resource expects the plural :users.

Solution 8 - Ruby on-Rails-3

Do This in routes.rb

devise_for :users, :controllers => {:registrations => "registrations"}, :skip => [:registrations]
  as :user do
    get 'users/edit' => 'devise/registrations#edit', :as => 'edit_user_registration'
    put 'users' => 'devise/registrations#update', :as => 'user_registration'
end

  devise_scope :user do
    get "/sign_in",  :to => "devise/sessions#new"
    get "/sign_up",  :to => "devise/registrations#new"
  end

you will get an error now while you come to sign in page, to fix it. Do this change in: app/views/devise/shared/_links.erb

<% if  request.path != "/sign_in" %>
    <%- if devise_mapping.registerable? && controller_name != 'registrations' %>
        <%= link_to "Sign up", new_registration_path(resource_name) %><br />
    <% end -%>
<% end %>

Solution 9 - Ruby on-Rails-3

I've found this to work well without messing with routes or adding application controller methods. My approach is to override the devise method. Add this to app/controllers/devise/registrations_controller.rb I've omitted the other methods for brevity.

class Devise::RegistrationsController < DeviseController
  ...
  # GET /resource/sign_up
  def new
    redirect_to root_path
  end
  ....
end

Also to remove illusion that this path is still reachable from other views you might also want to remove this code from app/views/devise/shared/_links.erb

<%- if devise_mapping.registerable? && controller_name != 'registrations' %>
  <%= link_to "Sign up", new_registration_path(resource_name) %><br />
<% end -%>

Solution 10 - Ruby on-Rails-3

For others in my case.
With devise (3.5.2).
I successfully removed the routes to signup, but kept the ones to edit the profile, with the following code.

#routes.rb
devise_for :users, skip: [:registrations]
as :user do
  get 'users/edit' => 'devise/registrations#edit', :as => 'edit_user_registration'
  put '/users(.:format)' => 'devise/registrations#update', as: 'user_registration'
  patch '/users(.:format)' => 'devise/registrations#update'
end

Solution 11 - Ruby on-Rails-3

Here's the slightly different route I went. It makes it so you don't have to override the devise/shared/_links.html.erb view.

In app/models/user.rb:

devise :database_authenticatable, :recoverable, :rememberable, :trackable, :validatable

In config/routes.rb:

devise_for :users
devise_scope :user do
  put 'users' => 'devise/registrations#update', as: 'user_registration'
  get 'users/edit' => 'devise/registrations#edit', as: 'edit_user_registration'
  delete 'users' => 'devise/registrations#destroy', as: 'registration'
end
Before:
$ rake routes | grep devise
           new_user_session GET    /users/sign_in(.:format)                    devise/sessions#new
               user_session POST   /users/sign_in(.:format)                    devise/sessions#create
       destroy_user_session DELETE /users/sign_out(.:format)                   devise/sessions#destroy
              user_password POST   /users/password(.:format)                   devise/passwords#create
          new_user_password GET    /users/password/new(.:format)               devise/passwords#new
         edit_user_password GET    /users/password/edit(.:format)              devise/passwords#edit
                            PATCH  /users/password(.:format)                   devise/passwords#update
                            PUT    /users/password(.:format)                   devise/passwords#update
   cancel_user_registration GET    /users/cancel(.:format)                     devise/registrations#cancel
          user_registration POST   /users(.:format)                            devise/registrations#create
      new_user_registration GET    /users/sign_up(.:format)                    devise/registrations#new
     edit_user_registration GET    /users/edit(.:format)                       devise/registrations#edit
                            PATCH  /users(.:format)                            devise/registrations#update
                            PUT    /users(.:format)                            devise/registrations#update
                            DELETE /users(.:format)                            devise/registrations#destroy
After:
$ rake routes | grep devise
           new_user_session GET    /users/sign_in(.:format)                    devise/sessions#new
               user_session POST   /users/sign_in(.:format)                    devise/sessions#create
       destroy_user_session DELETE /users/sign_out(.:format)                   devise/sessions#destroy
              user_password POST   /users/password(.:format)                   devise/passwords#create
          new_user_password GET    /users/password/new(.:format)               devise/passwords#new
         edit_user_password GET    /users/password/edit(.:format)              devise/passwords#edit
                            PATCH  /users/password(.:format)                   devise/passwords#update
                            PUT    /users/password(.:format)                   devise/passwords#update
          user_registration PUT    /users(.:format)                            devise/registrations#update
     edit_user_registration GET    /users/edit(.:format)                       devise/registrations#edit
               registration DELETE /users(.:format)                            devise/registrations#destroy

Solution 12 - Ruby on-Rails-3

Instead of searching for a hard solution. I used the below approaches.

  1. Delete the sign_up form from page (path devise/registrations/new.html.erb) and replace it with custom info.

  2. Redirect the incoming traffic to some other page. Like below in routes.rb

    get "/users/sign_up", to: redirect('/')

    post "/users/sign_up", to: redirect('/')

Make sure to write it before devise_for :users

Solution 13 - Ruby on-Rails-3

I had the same issue and I found it a bit bad practise to redirect users from the registration page. So my solution is basically is not using :registrable at all.

What I did was to create a similar page like edit user details which looked like:

<%= form_tag(update_user_update_path, method: :post) do %>  
    <br>
    <%= label_tag(:currPassword, 'Current password:') %> <%= password_field_tag(:currPassword) %> <br>
    <%= label_tag(:newPassword, 'New password:') %> <%= password_field_tag(:newPassword) %> <br>
    <%= label_tag(:newPasswordConfirm, 'Confirm new password:') %> <%= password_field_tag(:newPasswordConfirm) %> <br>
    <%= submit_tag('Update') %>
<% end %>

So this form submits into a new post end point that updates the password, which looks like:

  def update
    currPass = params['currPassword']
    newPass1 = params['newPassword']
    newPass2 = params['newPasswordConfirm']
    currentUserParams = Hash.new()
    currentUserParams[:current_password] = currPass
    currentUserParams[:password] = newPass1
    currentUserParams[:password_confirmation] = newPass2
    @result = current_user.update_with_password(currentUserParams)
  end

Later on you can use the @result in your view to tell the user whether the password is updated or not.

Solution 14 - Ruby on-Rails-3

By changing the routes there are a whole bunch of other problems that come with that. The easiest method I have found is to do the following.

ApplicationController < ActionController::Base
  before_action :dont_allow_user_self_registration

  private

  def dont_allow_user_self_registration
    if ['devise/registrations','devise_invitable/registrations'].include?(params[:controller]) && ['new','create'].include?(params[:action])
      redirect_to root_path
    end
  end
end

Solution 15 - Ruby on-Rails-3

You could modify the devise gem itself. First, run this command to find the installed location of using:

gem which devise

Let's suppose the path is: /usr/local/lib/ruby/gems/1.9.1/gems/devise-1.4.2/lib/devise

Then go to

/usr/local/lib/ruby/gems/1.9.1/gems/devise-1.4.2/lib/devise/lib/devise/rails and edit routes.rb in that directory. There is a method called def devise_registration(mapping, controllers) which you can modify to get rid of the new action. You can also completely remove the mappings for devise_registration

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionNathan LongView Question on Stackoverflow
Solution 1 - Ruby on-Rails-3stephenmurdochView Answer on Stackoverflow
Solution 2 - Ruby on-Rails-3Benoit GarretView Answer on Stackoverflow
Solution 3 - Ruby on-Rails-3equivalent8View Answer on Stackoverflow
Solution 4 - Ruby on-Rails-3DanielView Answer on Stackoverflow
Solution 5 - Ruby on-Rails-3MidnightView Answer on Stackoverflow
Solution 6 - Ruby on-Rails-3maxView Answer on Stackoverflow
Solution 7 - Ruby on-Rails-3dvanoniView Answer on Stackoverflow
Solution 8 - Ruby on-Rails-3SyedView Answer on Stackoverflow
Solution 9 - Ruby on-Rails-3lacostenycoderView Answer on Stackoverflow
Solution 10 - Ruby on-Rails-3MickaView Answer on Stackoverflow
Solution 11 - Ruby on-Rails-3bmaddyView Answer on Stackoverflow
Solution 12 - Ruby on-Rails-3Rajan Verma - AarvyView Answer on Stackoverflow
Solution 13 - Ruby on-Rails-3Sarp KayaView Answer on Stackoverflow
Solution 14 - Ruby on-Rails-3Weston GangerView Answer on Stackoverflow
Solution 15 - Ruby on-Rails-3Ankit SoniView Answer on Stackoverflow