I need to write a log when somebody failes to log in to my app (to track bruteforce attempts). Also I decided to log successful authentications. So I created a SessionsController < Devise::SessionsController and tried to override the sessions#create method like that: https://gist.github.com/3884693
The first part works perfectly, but when the auth failes rails throws some kind of an exception and never reaches the if statement. So I don't know what to do.
This answer to a previous SO question - Devise: Registering log in attempts has the answer.
The create action in the devise controller calls warden.authenticate!, which attempts to authenticate the user with the supplied params. If authentication fails then authenticate! will call the devise failure app, which then runs the SessionsController#new action. Note, any filters you have for the create action will not run if authentication fails.
So the solution is to add a filter after the new action which checks the contents of env["warden.options"] and takes the appropriate action.
I tried out the suggestion, and was able to log both the successful & failed login attempts. Here is the relevant controller code:
class SessionsController < Devise::SessionsController
after_filter :log_failed_login, :only => :new
def create
super
::Rails.logger.info "\n***\nSuccessful login with email_id : #{request.filtered_parameters["user"]}\n***\n"
end
private
def log_failed_login
::Rails.logger.info "\n***\nFailed login with email_id : #{request.filtered_parameters["user"]}\n***\n" if failed_login?
end
def failed_login?
(options = env["warden.options"]) && options[:action] == "unauthenticated"
end
end
The log has the following entries:
Started POST "/users/sign_in"
...
...
***
Successful login with email_id : {"email"=>...
***
...
...
Completed 302 Found
Started POST "/users/sign_in"
...
...
Completed 401 Unauthorized
Processing by SessionsController#new as HTML
...
...
***
Failed login with email_id : {"email"=>...
***
...
...
Completed 302 Found