Blog

< Back

Two Factor Authentication (2FA) in a Ruby on Rails App with Devise and Nexmo Verify

June 7, 2016 Published by
This is a guest post by Cristiano Betta
Cristiano Betta

I love applications that support two factor authentication! Whether it is through SMS, voice, or other means – it simply tells me that the app developer has been kind enough to think about my data and its security.

In 2015 more than 150 million user records were stolen and the leaked data proved that people still tend to use the same passwords across different sites. For those people the only thing standing between them and another compromised account is a good second factor authentication.

A little business app

For this tutorial I am going to show you how to add two factor authentication to your Rails app using the Nexmo Verify API. For this purpose I have built a little site called “Kittens & Co” – a social network where business cats can exchange their plans to take over the world.

Kittens & Co

You can download the starting point of the app from Github and run it locally.

Then visit 127.0.0.1:3000 in your browser and register.

By default the app implements registration and login using Devise but most of this tutorial applies similarly to apps that use other authentication methods. Additionally I added the bootstrap-sass and devise-bootstrap-templates gems for some prettyfication of our app.

All the code for this starting point can be found on the basic-login branch on Github. All the code I will be adding below can be found on the two-factor branch. For your convenience you can see all the changes between our start and end point on Github as well.

Nexmo Verify for 2FA

Nexmo Verify is phone verification made simple. Most two factor authentication plugins will require you to manage your own tokens, token expiry, retries, and SMS sending. Verify manages all of this for you and all you need to know are just 2 API calls!

To add Nexmo Verify to our system I am going to add the following changes:

  1. Add a phone_number to a User account
  2. Require verification on login if the user has a number on their account
  3. Verify the code sent to their number and log the user in

Adding a phone number

Add a phone number

Let’s start by adding a phone number to a user. We’ll do this by generating a new database
migration.

And then change it to add a new column to our user model.

Devise comes with the ability to edit a user right out of the box. By default these views are hidden, so we need to get a copy of them to make changes to. Devise makes this pretty easy through a Rails generator.

This will copy a lot of view templates into app/views/devise/. We need to delete most of them and only kept the one we really needed: registrations/edit.html.erb.

The only change we need to make to this template is to add a phone number field right after our email field.

The last step is to make Devise aware of this extra parameter. Without these lines the phone number won’t be accepted as a strong parameter and will be lost after it’s submitted.

To test this out, navigate to http://localhost:3000/users/sign_up, create an account, click on your email on the top right of the screen, enter your phone number and the password you used at signup and click Update. This will save your phone number to the database.

Send a 2FA verification request

Now that a user can add their phone number to their account we can have them verify their phone number on login. In order to send a verification message via Nexmo Verify we’re going to have to add the nexmo gem to our project.

As you can see we also need to add the dotenv-rails gem. This is so that the app can load our API credentials from a .env file. The Nexmo gem automatically picks up those environment variables and uses them to initialize the client. You can find your credentials on the settings page of your Nexmo account.

There are many different ways you could implement the verification check in your app. To keep things simple let’s add a before_action to our ApplicationController that checks if the user has two factor authentication enabled. If they do, make sure that they are verified before they are allowed to continue.

Let’s keep the code to see if the user requires verification very simple by checking if they have a phone number on file and if a :verified value on their session hasn’t been set yet.

To start the verification process, call send_verification_request (API call #1) on the Nexmo::Client object. We don’t need to pass in any API credentials because it has already been initialized through our environment values – though if you want to be explicit you can (see the gem documentation).

As you can see we pass the verification request the name of the web app. This is used in the text message the user receives and adds some very nice brand personalisation.

If the message has been sent successfully we redirect the user to a page to fill in the code they will receive. Obviously at this stage this would fail because we haven’t implemented this just yet.

Check 2FA verification code

Confirmation Screen

The final step is to confirm the code the user receives on their phone and set them as verified accordingly. For this we’re going to have to add a new page.

Let’s start with adding the routes.

And also create a basic controller.

As you can see we’ve made sure to skip the before_action we added to the ApplicationController earlier so that the browser doesn’t end up in an infinite loop of redirects.

Next, we should create a view so that when the user lands on the new page they are presented with a simple form to fill in their verification code.

The user then submits their code to the new update action. In this action we take the request_id and code from the params and pass them to the check_verification_request method (API call #2!) to verify them.

When a successful confirmation comes back we set the user’s status as verified and redirect them back to the main page. If the code was not successful we instead present the user with a message describing what went wrong. A full list of the response status codes can be found in the Verify documentation.

If you run the app, logout (if required), and login you’ll now be presented with the verify form.

And that’s it! I promised you, just 2 API calls.

Animated flow

Next steps

The Nexmo Verify API has a lot more options than we’ve covered here, ranging from searching and controlling the requests, to changing the code length and expiry time. Although the code I showed here is pretty simple I ended up with a very powerful out of the box experience. The system falls back to phone calls if needed, expires tokens without you having to do anything, prevents reuse of tokens, and logs verification times.

The Nexmo Ruby library is very agnostic as to how it’s used which means you could implement things very differently than I did here. For example, you could require the phone number on user registration, rejecting new accounts until the number has been validated.

I’d love to know what you’d add next? Please drop me a tweet (I’m @cbetta) with thoughts and ideas.

Nexmo_O

Tags: , , , ,

Categorised in: , ,

This post was written by