October 05, 2016

How to get extra security for your app using Devise and ThisData

If you're a Ruby on Rails developer, chances are you'll have used Devise in a project or two. Devise is a popular authentication library which adds a whole bunch of logic and pages to your app for logging in, sign up flows, password resets, and more.

ThisData is a login intelligence API which helps improve user security by detecting account takeovers and other anomalies. Let's look at how to get your Devise-powered app talking to ThisData's API.

When you get it up and running, here's what your Audit Log will end up looking like:

You could then go on to add "Was This You?" login alert emails, set up a security workflow using webhooks, create an in-app audit log for your users, and more!

Install ThisData's native ruby gem

Begin by adding our ruby gem to your project:

gem 'thisdata'  

We have a generator which will set some nice configuration options. Find your API key by going to your API Settings, or the Get Started page. Then run:

rails g this_data:install YOUR_API_KEY_HERE  

The generator will create a file called config/initializers/this_data.rb. If you need to do any further configuration or customization of ThisData, that's the place to do it! This might be things like telling our gem what methods to call when getting user IDs or email addresses for the current user.

Add some Warden hooks

Warden is the engine behind Devise, and it provides some callback hooks we want to latch on to. Create a file called lib/this_data/warden_hooks.rb, and add the following hooks:

Open config/initializers/devise.rb and add require 'this_data/warden_hooks'. This will add our hooks in when your app starts.

Devise.setup do |config|  
  ...
  require 'devise/orm/active_record'

  require 'this_data/warden_hooks'
  ...
end  

At this point you might want to restart your server to re-initialize the app.

What have we got?!

You'll now be tracking the following events:

  • log-in - successful login events
  • log-in-denied - when someone fails to log in
  • log-out - when the user logs out
  • page-view - each page viewed in your app, containing user information when they're logged in

But wait, there's more!

At the moment our failed login tracking is missing out on a bit of detail, and it's really easy to add that in. When a login is denied due to incorrect username and incorrect password, there's no user record for us to know about. But if the username is correct, we can send through details on that record. This will let you see brute force attacks against accounts in your ThisData Audit Log.

If you haven't already, you'll need to extend the default Devise SessionController.

Create a file app/controllers/my_sessions_controller.rb (or whatever you want to call it). If you have already extended Devise::SessionsController, simply copy the protected auth_options method over into your controller.

class MySessionsController < Devise::SessionsController

  protected

    # Extend Devise's basic auth_options Hash to add additional context about
    # the user. This makes it available to our Warden hooks.
    #
    # Returns a Hash
    def auth_options
      # Use Devise's first authentication method (e.g. email or username) to
      # get the sign in parameter
      authn_method = serialize_options(resource)[:methods].first
      authn_value  = sign_in_params[authn_method]

      # Look for a user matching that email/username
      user = resource_class.find_for_authentication(authn_method => authn_value)

      super.merge(
        sign_in_params: sign_in_params.except("password"),
        user: user
      )
    end

end  

In config/routes.rb, change your devise_for line to look like

devise_for :users, controllers: { :sessions => "my_sessions" }  

Now session requests will get a bit of extra information added to them while they pass through the rails stack, and we can make use of those in our Warden hooks. If the email/username is correct but the password is wrong, we'll now track the corresponding User details. Simple!

Go to town!

The more events you track, the more contextual and behavioural information ThisData can build upon to protect your users. With a simple ThisData.track({...}) method call in your extended Devise controllers, you can track events like

  • password-reset-request - someone asked to reset their password
  • password-reset - a User reset their password
  • password-reset-request
  • email-update
  • password-update

If you support Two Factor Authentication:

  • authentication-challenge
  • authentication-challenge-pass
  • authentication-challenge-fail
  • two-factor-disable

Further reading:

YOU MAY ALSO BE INTERESTED IN

Cloudbleed - ThisData's Response

Late last week Cloudflare announced that a pretty serious bug had been found in the way they handled their traffic. The bug allowed private ...