January 12, 2015
In this blog post, I'm going to show you how to do phone verification using SMS via Twilio. We will be using Twilio gem.
Let's create phone_number
, phone_verification_code
and phone_verified
columns in the users table.
$ bin/rails generate migration AddPhoneAttributesToUsers phone_number:string phone_verification_code:string phone_verified:boolean
Then run the migration.
Add phone_number
field in the registration form.
<%= f.text_field :phone_number %>
When a user submits registration form, we need to send SMS if
phone number is present. To handle this, add following code in User
model.
class User < ActiveRecord::Base
scope :unverified_phones, -> { where(phone_verified: false) }
before_save :set_phone_attributes, if: :phone_verification_needed?
after_save :send_sms_for_phone_verification, if: :phone_verification_needed?
def mark_phone_as_verified!
update!(phone_verified: true, phone_verification_code: nil)
end
private
def set_phone_attributes
self.phone_verified = false
self.phone_verification_code = generate_phone_verification_code
# removes all white spaces, hyphens, and parenthesis
self.phone_number.gsub!(/[\s\-\(\)]+/, '')
end
def send_sms_for_phone_verification
PhoneVerificationService.new(user_id: id).process
end
def generate_phone_verification_code
begin
verification_code = SecureRandom.hex(3)
end while self.class.exists?(phone_verification_code: verification_code)
verification_code
end
def phone_verification_needed?
phone_number.present? && phone_number_changed?
end
end
We have added 2 major changes in the user model,
set_phone_attributes
method is set in before_save
callback.send_sms_for_phone_verification
method is set in after_save
callback.In set_phone_attributes
method, we are setting up the phone attributes mainly
sanitizing phone number and generating unique phone verification code. In
send_sms_for_phone_verification
method, we send SMS to the user. Creation of
SMS for the phone verification message is handled in PhoneVerificationService
class.
class PhoneVerificationService
attr_reader :user
def initialize(options)
@user = User.find(options[:user_id])
end
def process
send_sms
end
private
def from
# Add your twilio phone number (programmable phone number)
Settings.twilio_number_for_app
end
def to
# +1 is a country code for USA
"+1#{user.phone_number}"
end
def body
"Please reply with this code '#{user.phone_verification_code}' to
verify your phone number"
end
def twilio_client
# Pass your twilio account SID and auth token
@twilio ||= Twilio::REST::Client.new(Settings.twilio_account_sid,
Settings.twilio_auth_token)
end
def send_sms
Rails.logger.info "SMS: From: #{from} To: #{to} Body: \"#{body}\""
twilio_client.account.messages.create(
from: from,
to: to,
body: body
)
end
end
In PhoneVerificationService
class, we have defined user
as an attribute reader
and in initialize
method, we have set the user object to it. Now if you see process
method, it does lots of stuff for us.
Lets go through each method.
from
- In this method, we set up the twilio phone number e.g.
programmable number.to
- In this method, we set the phone number to which we want to send
an SMS.body
- In this method, we build text message with verification code.twilio_client
- It creates twilio client based on twilio account SID and auth token.send_sms
- And last, it sends SMS to the user.As of now the system has the capability to send SMS to a user. Now we need to add capability to receive the SMS and to match the verification code.
First, we need to set up a request URL in Twilio account.
Open twilio account and under NUMBERS section/tab, click on your Twilio number. Then in Messaging section, add request URL with HTTP POST method. Add URL like this.
http://your.ngrok.com/phone_verifications/verify_from_message/
But to make it work, we need our Rails app on the public internet. There are two options for this:
I'm going to use Ngrok tunneling service for the purpose of this blog. You can check this blog for more about its usage.
We need one more controller, which will handle phone verification when request comes from Twilio. When a user replies back with a verification code, it will trigger request URL through Twilio API. To handle that request, let's add phone verifications controller.
$ bin/rails generate controller phone_verifications
Add new route in config/routes.rb
post "phone_verifications/verify_from_message" => "phone_verifications#verify_from_message"
Add following code to the PhoneVerificationsController
.
class PhoneVerificationsController < ApplicationController
skip_before_action :verify_authenticity_token
def verify_from_message
user = get_user_for_phone_verification
user.mark_phone_as_verified! if user
render nothing: true
end
private
def get_user_for_phone_verification
phone_verification_code = params['Body'].try(:strip)
phone_number = params['From'].gsub('+1', '')
condition = { phone_verification_code: phone_verification_code,
phone_number: phone_number }
User.unverified_phones.where(condition).first
end
end
In this controller, we added skip_before_action :verify_authenticity_token
,
this is because we need to allow twilio request in our Rails app which is
actually an outside request (e.g. not secured request). This means we have
disabled CSRF detection for this controller.
Now look at the verify_from_message
method, in this method we take phone verification
code and phone number from params hash. Using those data, we find the user from the database.
Once we get the user, then we mark user's phone number as a verified phone number.
Finally we are set to send business level text messages to the verified phone number.
This blog has more information about how to make secure twilio requests.
If this blog was helpful, check out our full blog archive.