How To Hack Devise and Sign Up Your Users via Ajax In Rails

Let’s say you want your users to sign up in order to leave a comment or do whatever other actions you want in your application. Well, it’s not a great experience if the user clicks on “Leave a Comment”, then gets redirected to the sign up page, then gets redirected back. What if, instead, you can get a nice sign up modal to pop up. The user signs up, the modal is hidden, and the user can leave a comment!

Well, this is exactly the functionality I was working on in my app, so I wanted to share how you can do it too.

Generate Devise Views

First, in case you’re not familiar with it, devise is an incredible rails gem that handles all user authentication stuff. Once you got the basic devise functionality setup by following the README, make sure to generate the devise views inside your own app as well. The wiki page explains it all, so I won’t go into detail on this part of the process.

Configure Devise To Accept JSON

First, we need to configure devise to accept a JSON request, which is what our AJAX will send it. To do that, simply change the following variables in your config/initializers/devise.rb file:

# config/initializers/devise.rb

config.http_authenticatable_on_xhr = false
config.navigational_formats = ["*/*", :html, :json]

Create A Registrations Controller

The sign up action happens from the devise registrations controller. You’ll have to overwrite the controller to respond with JSON. So just create a registrations controller, and here is an example of how you would overwrite it:

# app/controllers/registrations_controller.rb

class RegistrationsController < Devise::RegistrationsController

  def create
    build_resource

    if resource.save
      if resource.active_for_authentication?
        set_flash_message :notice, :signed_up if is_navigational_format?
        sign_up(resource_name, resource)
        return render :json => {:success => true}
      else
        set_flash_message :notice, :"signed_up_but_#{resource.inactive_message}" if is_navigational_format?
        expire_session_data_after_sign_in!
        return render :json => {:success => true}
      end
    else
      clean_up_passwords resource
      return render :json => {:success => false}
    end
  end

  # Signs in a user on sign up. You can overwrite this method in your own
  # RegistrationsController.
  def sign_up(resource_name, resource)
    sign_in(resource_name, resource)
  end

end

Keep in mind that you now have control over the json that will be returned, so you can customize it to whatever you think it should be, especially for when the sign up is unsuccessful due to validation errors.

Add Devise Mappings To Your Application Helper

Since devise is using the generic “resource” in it’s code, you need to map your user model to the devise resource. You can do that by adding the following to your applications helper or the helper file for the controller where you’d like to use the devise ajax functionality:


# app/helpers/application_helper.rb

module ApplicationsHelper
  def resource_name
    :user
  end

  def resource
    @resource ||= User.new
  end

  def devise_mapping
    @devise_mapping ||= Devise.mappings[:user]
  end
end

Redirect Routes!

In case you’ve tried the above steps and nothing changed, that’s because you have to redirect devise to use your controller instead of it’s own. This is done very easily in your routes file:

# config/routes.rb
  devise_for :users, :controllers => {registrations: 'registrations'}

Update Your Sign Up Form

This is where those devise views in your rails app become handy. The form_for helper accepts a :remote => true options, which automatically posts the form remotely (via ajax) for you, and the :format => :json option converts your form information into json. Here is what your form should look like (if you’re using haml):

- # app/views/devise/registrations/new.html.haml

%h2 Sign up
= form_for(resource, :as => resource_name,
           :url => registration_path(resource_name),
           :html => {:id => "sign_up_user"},
           :format => :json,
           :remote => true ) do |f|
  = devise_error_messages!
  %div
    = f.text_field :username, placeholder: "name"
  %div
    = f.email_field :email, placeholder: 'email'
  %div
    = f.password_field :password, placeholder: "password"
  %div
    = f.password_field :password_confirmation, placeholder: "password confirmation"
  %div= f.submit "Sign up", class: 'btn btn-success btn-medium'

Add The JavaScripts

And, of course, you need JavaScript to catch the ajax response from your server and handle it on the client side. Here is an example (in coffeescript):

# app/assets/javascripts/application.js.coffee

$("form#sign_up_user").bind "ajax:success", (e, data, status, xhr) ->
    if data.success
      $('#sign_up').modal('hide')
      $('#sign_up_button').hide()
      $('#submit_comment').slideToggle(1000, "easeOutBack" )
    else
      alert('failure!')

And now, everything should be working! Magic, right?!

Enjoy the article? Join over 14,500+ Swift developers and enthusiasts who get my weekly updates.

  • This is a great guide, thanks! One thing that some users may want to do is keep their old non-json forms, which is pretty easy to do using “respond_to do |format|” and “format.html { super }”. I see that your sign up form has the devise error messages. I am not clear on how those error messages ever show up. They must be sent through json, no? The login error handling is not too difficult because not as much can go wrong. But for registrations, where are those errors? Are they build if resource.save is false?

    • Great point, I ended up not using this for my project, so I didn’t go too far into error handling.

      For registrations, when you save your resource and it does not get saved, you can return the resource with the errors in json format (through the controller), which you can then parse and append to your html error fields with javascript.

  • Nice blog post! I have only a small comment to make: normally I think it is more semantic to return the resource and a status 201 when successful and the resource plus errors and a status 406 when failure.

    • Thanks, I just looked up status 201, and that should be returned on successful creation, so it seems more correct than a 200.

      I also agree with you on returning the resource, especially for errors. My point with the controller example was just to get you started on customizing it.

  • Vincent

    Can we already use current_user in rails server ?

  • William

    build_resource

    should be :

    build_resource(sign_up_params)

  • Micha

    $(“form#sign_up_user”).bind “ajax:success”, (e, data, status, xhr) ->

    Why this doesnt work on my js ? I have form_for in fancybox and when i try this – nothings happend

    • Archana

      This is what worked for me:

      $(document).on “ajax:success”, “form#sign_up_user”, (e, data, status, xhr)

  • Dima

    Thank you!

  • Dharin Rajgor

    I am not able to send Ajax Request if I enter the password in uppercase..It requests in HTML. Any idea ?

  • sp33c

    thank you! I use this to hookup rails4 with angular and only communicating via json.

  • Thou Like
  • andrewray

    This tutorial didn’t work for me. I wrote a complete set of instructions for how to accomplish this with much fewer lines of code: http://blog.andrewray.me/how-to-set-up-devise-ajax-authentication-with-rails-4-0/