I have a Rails 3.0 project using devise and I've been requested to register in DB every succesful login and every failed attempt.
From the devise documentation I think I'd have to extend FailureApp
but the examples are just redirecting users and not using the model at all. In stackoverflow I've just found this question but it remained unanswered, which is not encouraging
Can anyone tell me if I'm correct in this approach or it can't be done this way, or if there is some easier alternative I'm missing?
(I know there is no code yet, I'm just looking for a small guidance before diving in)
Thanks.
Modifying Devise::SessionsController to do your dirty work will do the trick.
Simply copy that file into your app/controllers/devise/sessions_controller.rb
Once you've done that, just add some code to where the user successfully logs in and where he fails to log in that will do what you want.
You'll probably want to create a new model for tracking login records.
Spent a little bit of time looking into this myself and figured someone else might find this useful.
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.
e.g.
def instrument_failed_login
instrument "failed_login.staff" if failed_login?
end
def failed_login?
(options = env["warden.options"]) && options[:action] == "unauthenticated"
end
An alternative and more maintainable way of adding logging around Devise authentication is to use the Warden Callbacks in an initializer. This is preferable because it uses the Warden API's designed for this and doesn't involve copy/pasting controller code.
Warden::Manager.before_failure do |env, opts|
logger.error("opts[:scope] authentication failure: #{opts[:message]}")
end
You can do the same with Warden::Manager#after_authentication
and Warden::Manager#before_logout
.