Back to Blog

Voice based phone verification using twilio

on March 18, 2015

In my previous blog post, I talked about how to do phone verification using SMS and now in this blog post I'm going to talk about how to do voice based phone verification using Twilio.

Requirement

Let's change the requirement a bit this time.

  • After the user signs up, send an SMS verification. If SMS verification goes well then use that phone number for future use.
  • But if the user's phone number doesn't support the SMS feature, then call on user's phone number for the verification.
  • In the call, ask the user to press 1 from keypad to complete the verification. If that user presses the 1 key, then mark the phone number as verified.

Step 1: Make a call for phone verification

We already handled SMS verification, so let's add call related changes in PhoneVerificationService class.

1class PhoneVerificationService
2  attr_reader :user, :verification_through
3
4  VERIFICATION_THROUGH_SMS  = :sms
5  VERIFICATION_THROUGH_CALL = :call
6
7  def initialize options
8    @user                 = User.find(options[:user_id])
9    @verification_through = options[:verification_through] || VERIFICATION_THROUGH_SMS
10  end
11
12  def process
13    if verification_through == VERIFICATION_THROUGH_SMS
14      perform_sms_verification
15    else
16      make_call
17    end
18  end
19
20  private
21
22  def from
23    #phone number given by twilio
24    Settings.twilio_number_for_app
25  end
26
27  def to
28    "+1#{user.phone_number}"
29  end
30
31  def body
32    "Please reply with this code '#{user.phone_verification_code}'" <<
33    "to verify your phone number"
34  end
35
36  def send_sms
37    Rails.logger.info "SMS: From: #{from} To: #{to} Body: \"#{body}\""
38
39    twilio_client.account.messages.create ( from: from, to: to, body: body)
40  end
41
42  def make_call
43    Rails.logger.info "Call: From: #{from} To: #{to}"
44
45    twilio_client.account.calls.create( from: from, to: to, url: callback_url)
46  end
47
48  def perform_sms_verification
49    begin
50      send_sms
51    rescue Twilio::REST::RequestError => e
52      return make_call if e.message.include?('is not a mobile number')
53      raise e.message
54    end
55  end
56
57  def callback_url
58    Rails.application.routes.url_helpers
59      .phone_verifications_voice_url(host: Settings.app_host,
60                                     verification_code: user.phone_verification_code)
61  end
62
63  def twilio_client
64    @twilio ||= Twilio::REST::Client.new(Settings.twilio_account_sid,
65                                         Settings.twilio_auth_token)
66  end
67end

In PhoneVerificationService class we have added some major changes:

  1. We have defined one more attribute reader verification_through and in initialize method we have set it.
  2. We have created two new constants VERIFICATION_THROUGH_SMS & VERIFICATION_THROUGH_CALL.
  3. We have changed process method and now we check which verification process should be taken place based on verification_through attribute.
  4. We also have added new methods perform_sms_verification, make_call and callback_url.

Let's go through these newly added methods.

perform_sms_verification:

In this method, first we try to send an SMS verification. If SMS verification fails with error message like 'is not a mobile number' then in the rescue block, we make a phone verification call or we raise the error.

make_call:

In this method, we create an actual phone call and pass the all required info to twilio client object.

callback_url:

In this method, we set the callback_url which is required for Twilio for making a call. When we call the user for verification, our app needs to behave like a human and should ask to the user to press 1 to complete the verification (i.e. In the form of TwiML). This callback_url needs be to set in our app.

Step 2: Add voice action in phone verification controller

For callback_url, add route for voice action in config/routes.rb

1post 'phone_verifications/voice' => 'phone_verifications#voice'

Add voice action and required code for it in PhoneVerificationsController.

1class PhoneVerificationsController < ApplicationController
2  skip_before_filter :verify_authenticity_token
3  after_filter :set_header
4
5  HUMAN_VOICE = 'alice'
6
7  def voice
8    verification_code = params[:verification_code]
9
10    response = Twilio::TwiML::Response.new do |r|
11      r.Gather numDigits: '1',
12               action: "/phone_verifications/verify_from_voice?verification_code=#{verification_code}",
13               method: 'post' do |g|
14
15        g.Say 'Press 1 to verify your phone number.', voice: HUMAN_VOICE
16      end
17    end
18
19    render_twiml response
20  end
21
22  def verify_from_message
23    user = get_user_for_phone_verification
24    user.mark_phone_as_verified! if user
25
26    render nothing: true
27  end
28
29  private
30
31  def get_user_for_phone_verification
32    phone_verification_code = params['Body'].try(:strip)
33    phone_number            = params['From'].gsub('+1', '')
34
35    condition = { phone_verification_code: phone_verification_code,
36                  phone_number: phone_number }
37
38    User.unverified_phones.where(condition).first
39  end
40
41  def set_header
42    response.headers["Content-Type"] = "text/xml"
43  end
44
45  def render_twiml(response)
46    render text: response.text
47  end
48end

In this voice method, we have set up the Twilio response. When a call is made to the user, this response will get converted into robotic human voice. render_twiml method which sets twilio response in text form is required for Twilio APIs. Set up the response header in the set_header method which gets called in after_filter method.

Step 3: Set request URL for voice phone verification in Twilio

In voice action method, we have setup the request url in the action key of Twilio response object, that also needs to be set in your Twilio account. So when, user replies back to the call query, Twilio will make a request to our app and adds its own some values as parameters to the request. Using those parameters we handle the actual phone verification in the app.

Open twilio account and under NUMBERS section/tab, click on your Twilio number. Then in Voice section, add request URL with HTTP POST method. Add URL like this.

http://your.ngrok.com/phone_verifications/verify_from_voice/

We need Ngrok to expose local url to external world. Read more about it in my previous blog post.

Step 4: Add verify_from_voice action in phone verification controller

First add route for this action in config/routes.rb

1post "phone_verifications/verify_from_voice" => "phone_verifications#verify_from_voice

Add this method, in your PhoneVerificationsController.

1  def verify_from_voice
2    response = Twilio::TwiML::Response.new do |r|
3      if params['Digits'] == "1"
4        user = get_user_for_phone_verification
5        user.mark_phone_as_verified!
6
7        r.Say 'Thank you. Your phone number has been verified successfully.',
8               voice: HUMAN_VOICE
9      else
10        r.Say 'Sorry. Your phone number has not verified.',
11               voice: HUMAN_VOICE
12      end
13    end
14
15    render_twiml response
16  end

Modify private method get_user_for_phone_verification to support voice verification in PhoneVerificationsController.

1  def get_user_for_phone_verification
2    if params['Called'].present?
3      phone_verification_code = params['verification_code']
4      phone_number            = params['To']
5    else
6      phone_verification_code = params['Body'].try(:strip)
7      phone_number            = params['From']
8    end
9    condition = { phone_verification_code: phone_verification_code,
10                  phone_number: phone_number.gsub('+1', '') }
11
12    User.unverified_phones.where(condition).first
13  end

In the verify_from_voice method, we get parameters Digits, To & verification_code from Twilio request. Using these parameters, we search for user in the database. If we get the proper user then we mark user's phone number as verified phone number.


You might also like

If you liked this blog post, check out similar ones from BigBinary