Verify user phone numbers in Ruby on Rails with the Authy Phone Verification API

August 30, 2017
Written by
Phil Nash
Twilion

header

Update January 2022

For new development, we encourage you to use the Verify API instead of the Authy API. The Verify API is an evolution of the Authy API with continued support for SMS, voice, and email one-time passcodes, an improved developer experience and new features including:

This blog post uses the Authy API. Any new features and development will be on the Verify API. Check out the FAQ for more information and Verify API Reference to get started.

If you ever need to contact your application’s users by phone then it’s good practice to verify that the phone number they enter is in fact theirs. Let’s see how to verify phone numbers in a Rails 5 application using the Authy Phone Verification API.

What you’ll need

To code along with this post, you’ll need:

Getting started

Create a new Rails application for this project:

rails new phone_verification
cd phone_verification

We need to add a couple of gems that we’ll use in the project, open up the Gemfile and add authy and envyable. Authy is going to be used for the phone verification, envyable to manage environment variables.

gem 'envyable'
gem 'authy'

Install the gems, then initialise envyable from the command line:

bundle install
bundle exec envyable install

Open up config/env.yml and add your Authy app API key:

AUTHY_API_KEY: YOUR_API_KEY_HERE

Now, initialise Authy itself by creating the file config/initializers/authy.rb and add the following code:

Authy.api_key = ENV['AUTHY_API_KEY']
Authy.api_uri = 'https://api.authy.com/'

That is all the initialisation we need, we can now get on with building the application.

Building the app

The process of verification is quite straightforward.

  • The user enters three bits of information
    • Phone number
    • Country code
    • Whether they want to verify by voice or SMS
  • Our application makes a request to the API to initiate the verification
  • The user receives a call or text message with a 4 digit code
  • They then enter that code into a form in the application
  • The code is sent off to the API, with their phone number and country code again, to verify
  • If it is correct then the user has verified their number

We’re going to need a controller that will both present the views we need as well as do the work to initialise the verification and actually verify the phone number. Generate a controller with:

bundle exec rails generate controller phone_verifications new create challenge verify success

This will create five actions and five views. We don’t need the create and verify views, so you can delete those. It will also generate some routes. Open config/routes.rb remove the 5 new routes and replace them with:

  resources :phone_verifications, :only => [:new, :create] do |p|
    collection do
      get 'challenge'
      post 'verify'
      get 'success'
    end
  end

This creates 5 routes which you can check out by running rails routes on the command line. They look like this:

bundle exec rails routes
                       Prefix Verb URI Pattern                              Controller#Action
challenge_phone_verifications GET  /phone_verifications/challenge(.:format) phone_verifications#challenge
   verify_phone_verifications POST /phone_verifications/verify(.:format)    phone_verifications#verify
  success_phone_verifications GET  /phone_verifications/success(.:format)   phone_verifications#success
          phone_verifications POST /phone_verifications(.:format)           phone_verifications#create
       new_phone_verification GET  /phone_verifications/new(.:format)       phone_verifications#new

 

The first view

The new action needs to display a form to the user that takes their phone number, the country code of the country they are in and whether they want the verification by SMS or voice call.

To generate the list of country codes and make an interface that we can use to search by country, include the Authy JavaScript form helpers. Add to the of app/views/layouts/application.html.erb

    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= stylesheet_link_tag    'https://cdnjs.cloudflare.com/ajax/libs/authy-form-helpers/2.3/form.authy.min.css', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'https://cdnjs.cloudflare.com/ajax/libs/authy-form-helpers/2.3/form.authy.min.js', 'data-turbolinks-track': 'reload' %>

Now, in app/views/phone_verifications/new.html.erb replace the code that was generated with the following:

<h1>Verify your phone number</h1>

<%= form_tag(phone_verifications_path, method: :post) do -%>
  <% if @response %>
    <% @response.errors.each do |key, message| %>
      <p><%= message %></p>
    <% end %>
  <% end %>
  <div>
    <%= label_tag "authy-countries", "Country code:" %>
    <%= select_tag "authy-countries", nil, name: 'country_code', 'data-show-as': 'number' %>
  </div>
  <div>
    <%= label_tag "phone_number", "Phone number:" %>
    <%= telephone_field_tag "phone_number", nil, name: 'phone_number' %>
  </div>
  <div>
    <p>Verification method</p>
    <%= label_tag "method_sms", "SMS: " %>
    <%= radio_button_tag "method", "sms" %>
    <%= label_tag "method_call", "Call: " %>
    <%= radio_button_tag "method", "call" %>
  </div>
  <%= button_tag "Verify" %>
<% end -%>

This code sets up the form to submit the three fields. Using the ID "authy-countries" means that the select_tag will be picked up by the Authy JavaScript and augmented to become a searchable list of countries.

We also use telephone_field_tag to create an HTML input with a type of "tel" so that mobile devices show an optimised keyboard for entering telephone numbers.

Start up the server:

bundle exec rails server

Navigate to http://localhost:3000/phone_verifications/new to see the new form and try out the country code field.

When you start typing in the field, flags, numbers and country names show up. When you select one, the country code is left in the input box.

The verification process

Now we have our form to start the process, we need to begin the verification. Using the parameters entered into the form, we will build an API request to the Authy phone verification API and send it off. If it is successful we will present the form asking for the verification code, otherwise we should re-render the initial form and show the error.

In app/controllers/phone_verifications_controller.rb replace the empty create method with this code:

  def create
    session[:phone_number] = params[:phone_number]
    session[:country_code] = params[:country_code]
    @response = Authy::PhoneVerification.start(
      via: params[:method],
      country_code: params[:country_code],
      phone_number: params[:phone_number]
    )
    if @response.ok?
      redirect_to challenge_phone_verifications_path
    else
      render :new
    end
  end

The key part here is the call to Authy::PhoneVerification.start. We pass the parameters we gathered from the user, including the way to contact them, the phone number and the country code. If the response to this is ok, then the verification process will start, otherwise there is an issue.

We already built in the code to display errors if something does go wrong. These lines in the view handle that:

  <% if @response %>
    <% @response.errors.each do |key, message| %>
      <p><%= message %></p>
    <% end %>
  <% end %>

Note too, that we save the phone number and country code to the session so that we can use them later. In a full application these would be saved against a user or phone number record in the database and we’d keep the ID of the record in the session.

The challenge

We now need users to enter the 4 digits that the verification process sends them. Add the following code to app/views/phone_verifications/challenge.html.erb:

<h1>Challenge</h1>

<%= form_tag(verify_phone_verifications_path, method: :post) do -%>
  <% if @response %>
    <% @response.errors.each do |key, message| %>
      <p><%= message %></p>
    <% end %>
  <% end %>
  <div>
    <%= label_tag "code", "Enter the code you were sent:" %>
    <%= text_field_tag "code" %>
  </div>
  <%= button_tag "Verify" %>
<% end -%>

 

Time to verify

Now we have the user’s 4 digit code we need to verify it with the API. Replace the empty verify action in the PhoneVerificationsController with:

  def verify
    @response = Authy::PhoneVerification.check(
      verification_code: params[:code],
      country_code: session[:country_code],
      phone_number: session[:phone_number]
    )
    if @response.ok?
      session[:phone_number] = nil
      session[:country_code] = nil
      redirect_to success_phone_verifications_path
    else
      render :challenge
    end
  end

This time the key part here is making the request to Authy::PhoneVerification.check passing in the phone number and country code that we saved earlier along with the code the user entered. If the response is ok this time, then the verification was a success and we can clear the session and redirect to the success path. In a real application you would likely mark that the user or phone record was indeed verified and save that in the database too.

If the code was wrong, we’ll just show the challenge view again so that the user can try again if they made a mistake.

To finish off, add a success message to app/views/phone_verifications/success.html.erb:

<h1>Congratulations!</h1>
<p>You have successfully verified your phone number.</p>

 

Let’s test it out

If you didn’t start the server earlier, do so now:

bundle exec rails server

Go to http://localhost:3000/phone_verifications/new and enter the correct details. When you get the phone call or SMS message, enter the verification code into the second form and you will see a message of success that your phone is verified.

The whole process from start to finish: the user enters their phone number, country code and checks SMS, then they are directed to the challenge page where they enter a 4 digit code which is correct so directs them to the congratulations page.

Phone numbers? Verified!

That’s how to get started with the Authy Phone Verification API in Rails 5. You can check out the full code for this article on GitHub. This application is far from complete of course, there is much more to do. You’d want to:

  • save the phone number and its verification status to a user model in your database
  • handle errors better, giving users the chance to resend or call again or check and update the phone number

If you’ve got any questions or comments, then please do get in touch. Leave me a comment below or give me a shout on Twitter.