Devise - Sign In with Ajax
1. Generate Devise controllers so we can modify it
rails g devise:controllers
Now we have all controllers in the app/controllers/[model] directory
2. Edit routes.rb
Let's set Devise to use our modified SessionsController
First add this code (of course change :users to your devise model) into config/routes.rb
devise_for :users, controllers: { sessions: 'users/sessions'}
3. Modify sessions_controller.rb
Find the create method and change it to
def create resource = User.find_for_database_authentication(email: params[:user][:email]) return invalid_login_attempt unless resource if resource.valid_password?(params[:user][:password]) sign_in :user, resource return render nothing: true end invalid_login_attempt end
Create new method after protected
def invalid_login_attempt set_flash_message(:alert, :invalid) render json: flash[:alert], status: 401end
4. devise.rb
Insert this into config/initializers/devise.rb
config.http_authenticatable_on_xhr = falseconfig.navigational_formats = ["*/*", :html, :json]
5. Invalid email or password message
Insert a new message into config/locales/devise.en.yml under the sessions
invalid: "Invalid email or password."
6. View
= form_for resource, url: session_path(:user), remote: true do |f| = f.text_field :email = f.password_field :password = f.label :remember_me do Remember me = f.check_box :remember_me = f.submit value: 'Sign in':javascript $(document).ready(function() { //form id $('#new_user') .bind('ajax:success', function(evt, data, status, xhr) { //function called on status: 200 (for ex.) console.log('success'); }) .bind("ajax:error", function(evt, xhr, status, error) { //function called on status: 401 or 500 (for ex.) console.log(xhr.responseText); }); });
Important thing remote: true
The reason why I am using status 200 or 401 unlike {status: 'true'} is less data size, so it is much faster and cleaner.
Explanation
On signing in, you get these data in params
action: "create"commit: "Sign in"controller: "users/sessions"user: { email: "test@test.cz" password: "123" remember_me: "0"}utf8: "✓"
Before signing, you need to authorize the user.
resource = User.find_for_database_authentication(email: params[:user][:email])
User.find_for_database_authentication
If user is found, resource will be filled with something like
created_at: "2015-05-29T12:48:04.000Z"email: "test@test.cz"id: 1updated_at: "2015-06-13T19:56:54.000Z"
Otherwise will be
null
If the user is authenticated, we are about to validate his password
if resource.valid_password?(params[:user][:password])
And finally sign in
sign_in :user, resource
Sources
Helped meAndreas Lyngstad