What's happening is that the sign_in
method is breaking you out of the normal flow by throwing a warden error, which will call the failure app.
If you look at the definition of sign_in
in lib/devise/controllers/helpers.rb
, you'll see that in a normal flow where you're signing in a user for the first time, you wind up calling
warden.set_user(resource, options.merge!(:scope => scope)
warden
is a reference to a Warden::Proxy
object, and if you look at what set_user
does (you can see that at warden/lib/warden/proxy.rb:157-182
), you'll see that after serializing the user into the session it runs any after_set_user
callbacks.
Devise defines a bunch of these in lib/devise/hooks/
, and the particular one we're interested is in lib/devise/hooks/activatable.rb
:
Warden::Manager.after_set_user do |record, warden, options|
if record && record.respond_to?(:active_for_authentication?) && !record.active_for_authentication?
scope = options[:scope]
warden.logout(scope)
throw :warden, :scope => scope, :message => record.inactive_message
end
end
As you can see, if the record is not active_for_authentication?
, then we throw
. This is what is happening in your case -- active_for_authentication?
returns false for a confirmable
resource that is not yet confirmed (see lib/devise/models/confirmable.rb:121-127
).
And when we throw :warden
, we end up calling the devise failure_app
. So that's what's happening, and why you're breaking out of the normal control flow for your controller.
(Actually the above is talking about the normal sessions controller flow. I think your js
block is actually redundant -- calling warden.authenticate!
will set the user as well, so I think you're throwing before you even get to sign_in
.)
To answer your second question, one possible way of handling this is to create your own failure app. By default devise sets warden's failure_app
to Devise::Delegator
, which allows you to specify different failure apps for different devise models, but defaults to Devise::FailureApp
if nothing has been configured. You could either customize the existing failure app, replace it with your own failure app by configuring warden, or you could customize the delegator to use the default failure app for html requests and delegate to a different failure app for json.