ruby-on-railsdevisewarden

Devise API sign_in doesn't work with postman


I'm using Rails 6 in API mode and the latest version of devise.

I created rspec test for sign_in and sign_up and it works correctly they accept json answer and respond in json

When I try to test my API with postman it work for sign_up but not for sign_in.

With postman I got the following return

{
    "sucess": false,
    "message": "fail myapp_failure_app"
}

So I try to dig in the devise gem the problem and I finally found out the problem is here

def create
    self.resource = warden.authenticate!(auth_options)
    ...
end

And in the warden gem the problem occured at the method _run_callbacks(*args)

def _run_callbacks(*args) #:nodoc:
  self.class._run_callbacks(*args)
end

Arguments passe to this method are:

[:after_set_user, #<User id: 4, nickname: "max", email: "test@test", birth_at: nil, created_at: "2020-01-12 22:44:19", updated_at: "2020-01-13 21:44:57">, Warden::Proxy:70223361236700 @config={:default_scope=>:user, :scope_defaults=>{}, :default_strategies=>{:user=>[:jwt, :rememberable, :database_authenticatable]}, :intercept_401=>false, :failure_app=>MyappFailureApp}, {:scope=>:user, :recall=>"sessions#new", :store=>true, :event=>:authentication}]

If I change the method to this

def _run_callbacks(*args) #:nodoc:
  return
  self.class._run_callbacks(*args)
end

It's working well.

I expected to find the solution by myself with digging in the gem and comparing my results with my spec but I honestly don't understand what this methods really do and why it's different with my spec.


Solution

  • Devise defines multiple hooks on warden, which are invoked via _run_callbacks method. It looks like you have a failure in one of those callback hooks. The hooks can be found here in the Devise source. Also your MyappFailureApp isn't really displaying the error, would it be possible to define a nicer one ? Something like -

    class MyappFailureApp < Devise::FailureApp
      def respond
        http_auth
      end
    
      def http_auth
        self.status = 401
        self.headers["WWW-Authenticate"] = %(Bearer realm=#{Devise.http_authentication_realm.inspect}) if http_auth_header?
        self.content_type = 'application/json'
        self.response_body = http_auth_body
      end
    
      def http_auth_body
        { error: [i18n_message] }.to_json
      end
    
      def request_format
        :json
      end
    end