ruby-on-railsjsondeviseruby-on-rails-5

Rails 5 Devise: Return JSON On sign_in Failure


Everything I seem to find on this issue is out of date and/or doesn't work.

GOAL: When a user tries to sign in via JSON from a mobile application and the username or password is wrong, I would like Rails to return JSON data so the errors can be displayed on the app.

So far I have done the following:

class Users::SessionsController < Devise::SessionsController
  # before_action :configure_sign_in_params, only: [:create]
  skip_before_action :verify_authenticity_token
  respond_to :json

  # POST /resource/sign_in
  def create
    self.resource = warden.authenticate!(auth_options)
    set_flash_message(:notice, :signed_in) if is_flashing_format?
    sign_in(resource_name, resource)
    yield resource if block_given?
    respond_with resource, :location => after_sign_in_path_for(resource) do |format|
      format.json {render :json => resource } # this code will get executed for json request
    end
  end
end

This works well on success, but I'm not sure what to do when it fails. Right now it returns an undefined method error:

undefined method `users_url' for #<Users::SessionsController:0x0000000195fa28>

Solution

  • Figured it out. CustomFailure never seemed to work, no matter how many things I tried. I was able to do this within the SessionsController via handle_failed_login:

    class Users::SessionsController < Devise::SessionsController
      # before_action :configure_sign_in_params, only: [:create]
      after_filter :handle_failed_login, :only => :new
      skip_before_action :verify_authenticity_token
      respond_to :json
    
      # POST /resource/sign_in
      def create
        self.resource = warden.authenticate!(auth_options)
        set_flash_message(:notice, :signed_in) if is_flashing_format?
        sign_in(resource_name, resource)
        yield resource if block_given?
        respond_with resource, :location => after_sign_in_path_for(resource) do |format|
          format.json {render :json => resource } # this code will get executed for json request
        end
      end
    
      private
      def handle_failed_login
        if failed_login?
          render json: { success: false, errors: ["Login Credentials Failed"] }, status: 401
        end
      end 
    
      def failed_login?
        (options = env["warden.options"]) && options[:action] == "unauthenticated"
      end 
    end