- DeviseZola2015-05-13T00:00:00+00:00https://www.fullstackstanley.com/tags/devise/atom.xmlVerifying mobile phone numbers with Rails, Devise and Twilio2015-05-13T00:00:00+00:002015-05-13T00:00:00+00:00https://www.fullstackstanley.com/articles/verifying-mobile-phone-numbers-with-rails-devise-and-twilio/<p>This article will cover setting up a new Rails (4.2) application with Devise and allow users to add their mobile phone number to their account.</p>
<span id="continue-reading"></span>
<p>We'll use the <a href="https://github.com/daddyz/phonelib">phonelib</a> gem to validate numbers and then send a 6 digit code via <a href="https://www.twilio.com/">Twilio</a> to the user's mobile to verify that it's theirs.</p>
<p>I'll be starting afresh with a new Rails application.</p>
<pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>rails new verify
</span></code></pre>
<h2 id="dependancies">Dependancies</h2>
<p>Add the following dependancies to your <code>Gemfile</code></p>
<pre data-lang="ruby" style="background-color:#2b303b;color:#c0c5ce;" class="language-ruby "><code class="language-ruby" data-lang="ruby"><span style="color:#8fa1b3;">gem </span><span>"</span><span style="color:#a3be8c;">devise</span><span>"</span><span style="color:#8fa1b3;">, </span><span>'</span><span style="color:#a3be8c;">~>3.4.1</span><span>'
</span><span style="color:#8fa1b3;">gem </span><span>'</span><span style="color:#a3be8c;">twilio-ruby</span><span>'</span><span style="color:#8fa1b3;">, </span><span>'</span><span style="color:#a3be8c;">~> 4.0.0</span><span>'
</span><span style="color:#8fa1b3;">gem </span><span>'</span><span style="color:#a3be8c;">phonelib</span><span>'
</span><span style="color:#8fa1b3;">gem </span><span>'</span><span style="color:#a3be8c;">dotenv-rails</span><span>'</span><span style="color:#8fa1b3;">, </span><span style="color:#a3be8c;">:groups </span><span style="color:#8fa1b3;">=> [</span><span style="color:#a3be8c;">:development</span><span style="color:#8fa1b3;">, </span><span style="color:#a3be8c;">:test</span><span style="color:#8fa1b3;">] </span><span style="color:#65737e;"># optional
</span></code></pre>
<p>Run <code>bundle</code> to install the new gems.</p>
<p>Devise is the authentication system we'll be using.</p>
<p>Twilio-ruby will be used to send text messages. Make sure you sign up at <a href="https://www.twilio.com/">Twilio</a> and make a note of your test credentials.</p>
<p>Note that when you use test credentials you will not receive a text. You'll only receive an API response from Twilio.</p>
<p><a href="https://github.com/daddyz/phonelib">Phonelib</a> is a gem which validates phone numbers based on Google libphonenumber.</p>
<p><a href="https://github.com/bkeepers/dotenv">Dotenv</a> let's you set environment variables in development. I use this to set Twilio test credentials but you can set them your own preferred way if you wish.</p>
<h2 id="setting-up-devise">Setting up Devise</h2>
<p>Run devise:install to set it up.</p>
<pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>rails generate devise:install
</span></code></pre>
<p>Make sure you have a home route in <code>config/routes.rb</code> as described in the post-installation command notes.</p>
<pre data-lang="ruby" style="background-color:#2b303b;color:#c0c5ce;" class="language-ruby "><code class="language-ruby" data-lang="ruby"><span>root </span><span style="color:#a3be8c;">to: </span><span>"</span><span style="color:#a3be8c;">home#index</span><span>"
</span></code></pre>
<p>Add flash messages to your <code>app/views/application.html.erb</code>.</p>
<p><em>Source <a href="http://stackoverflow.com/a/17932167">StackOverflow</a>.</em></p>
<pre data-lang="html" style="background-color:#2b303b;color:#c0c5ce;" class="language-html "><code class="language-html" data-lang="html"><span><%# Rails flash messages styled for Bootstrap 3.0 %>
</span><span><% flash.each do |name, msg| %>
</span><span> <% if msg.is_a?(String) %>
</span><span> <</span><span style="color:#bf616a;">div </span><span style="color:#d08770;">class</span><span>="</span><span style="color:#a3be8c;">alert alert-<%= name.to_s == 'notice' ? 'success' : 'danger' %></span><span>">
</span><span> <</span><span style="color:#bf616a;">button </span><span style="color:#d08770;">type</span><span>="</span><span style="color:#a3be8c;">button</span><span>" </span><span style="color:#d08770;">class</span><span>="</span><span style="color:#a3be8c;">close</span><span>" </span><span style="color:#d08770;">data-dismiss</span><span>="</span><span style="color:#a3be8c;">alert</span><span>" </span><span style="color:#d08770;">aria-hidden</span><span>="</span><span style="color:#a3be8c;">true</span><span>"></span><span style="color:#8fa1b3;">&</span><span style="color:#d08770;">times;</span><span></</span><span style="color:#bf616a;">button</span><span>>
</span><span> <%= content_tag :div, msg, :id => "flash_#{name}" %>
</span><span> </</span><span style="color:#bf616a;">div</span><span>>
</span><span> <% end %>
</span><span><% end %>
</span></code></pre>
<p>Generate your Devise model. I prefer to call mine <code>User</code>.</p>
<pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>rails generate devise User
</span></code></pre>
<p>Before you run the migration command add these three columns to <code>db/migrate/20150509173432_devise_create_users.rb</code></p>
<pre data-lang="ruby" style="background-color:#2b303b;color:#c0c5ce;" class="language-ruby "><code class="language-ruby" data-lang="ruby"><span>t.string </span><span style="color:#a3be8c;">:mobile_number
</span><span>t.string </span><span style="color:#a3be8c;">:verification_code
</span><span>t.boolean </span><span style="color:#a3be8c;">:is_verified
</span></code></pre>
<p>At this point you can run <code>rake db:migrate</code>.</p>
<p><code>mobile_number</code> will hold each user's mobile number. <code>verification_code</code> will hold the generated code that you will send to their phone. <code>is_verified</code> is a boolean which will become true after a successful verification.</p>
<p>At this point you need to make a user. You can either run the server and go to <code>http://localhost:3000/users/sign_up</code> or do it with <code>rails console</code> and this code: </p>
<pre data-lang="ruby" style="background-color:#2b303b;color:#c0c5ce;" class="language-ruby "><code class="language-ruby" data-lang="ruby"><span style="color:#ebcb8b;">User</span><span>.create!({</span><span style="color:#a3be8c;">email: </span><span>"</span><span style="color:#a3be8c;">example@email.com</span><span>", </span><span style="color:#a3be8c;">password: </span><span>"</span><span style="color:#a3be8c;">test12345</span><span>"})
</span></code></pre>
<p>Log in at <code>http://localhost:3000/users/sign_in</code> with your credentials.</p>
<p><em>Note that I haven't made a home controller or a home view. You will see an error when after logging in if you haven't either. It's not important for this article so long as you have successfully logged in.</em></p>
<h2 id="adding-a-mobile-number">Adding a mobile number</h2>
<p>We need to add a new form to the user's edit profile page which allows a user to add their mobile number. To do this we need to publish the devise views so we can override them.</p>
<pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>rails g devise:views
</span></code></pre>
<p>Add a new field to the form in <code>app/views/devise/registrations/edit.html.erb</code>.</p>
<pre data-lang="html" style="background-color:#2b303b;color:#c0c5ce;" class="language-html "><code class="language-html" data-lang="html"><span> <</span><span style="color:#bf616a;">div </span><span style="color:#d08770;">class</span><span>="</span><span style="color:#a3be8c;">field</span><span>">
</span><span> <%= f.label :mobile_number %><</span><span style="color:#bf616a;">br </span><span>/>
</span><span> <%= f.text_field :mobile_number %>
</span><span> </</span><span style="color:#bf616a;">div</span><span>>
</span><span>```
</span><span>
</span><span>We will validate mobile numbers before we try to send texts to them. Earlier we installed the gem phonelib which we'll set up now.
</span><span>
</span><span>In ```app/models/user.rb``` add the following validation.
</span><span>
</span><span>~~~ruby
</span><span> validates_uniqueness_of :mobile_number
</span><span> validates :mobile_number, phone: { possible: false, allow_blank: true, types: [:mobile] }
</span></code></pre>
<p><code>validates_uniqueness_of</code> will make sure no two users have the same mobile number.</p>
<p>In the <code>validates</code> method we set up phonelib with <code>possible: false</code> which, if set to true, enables a faster validation which is less strict. <code>allow_blank</code> means a user can enter no number at all. and <code>types</code> sets the types of numbers that are valid. Since we're sending texts I have set it to <code>mobile</code>. There are lots of types including <code>voip</code> and <code>personal_number</code>. You can see a full list in the gem's <a href="https://github.com/daddyz/phonelib">read me</a>.</p>
<p>If you need to restrict the country you can do so by creating <code>config/initializers/phonelib.rb</code> and adding this code</p>
<pre data-lang="ruby" style="background-color:#2b303b;color:#c0c5ce;" class="language-ruby "><code class="language-ruby" data-lang="ruby"><span style="color:#ebcb8b;">Phonelib</span><span>.default_country = "</span><span style="color:#a3be8c;">GB</span><span>"
</span></code></pre>
<p>Since I am from the UK I will be using GB (Great Britain).</p>
<p><strong>Make sure you restart your rails server after adding this setting.</strong></p>
<p>At this point we need to add <code>mobile_number</code> to Devise's list of acceptable parameters. The easiest way to do this is to override them in the registration controller. Run the following command to generate all Devise controllers so that we can override them.</p>
<pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>rails g devise:controllers acme
</span></code></pre>
<p><code>acme</code> is the module and directory to place the new controllers. Call it what you prefer.</p>
<p>You can remove all of the newly generated controllers except <code>app/controllers/acme/registrations_controller.rb</code>.</p>
<p>Amend your devise route in <code>config/routes.rb</code> like so:</p>
<pre data-lang="ruby" style="background-color:#2b303b;color:#c0c5ce;" class="language-ruby "><code class="language-ruby" data-lang="ruby"><span> devise_for </span><span style="color:#a3be8c;">:users</span><span>, </span><span style="color:#a3be8c;">:controllers </span><span>=> { </span><span style="color:#a3be8c;">:registrations </span><span>=> "</span><span style="color:#a3be8c;">acme/registrations</span><span>"}
</span></code></pre>
<p>Now in <code>app/controllers/acme/registrations_controller.rb</code> add the following code.</p>
<pre data-lang="ruby" style="background-color:#2b303b;color:#c0c5ce;" class="language-ruby "><code class="language-ruby" data-lang="ruby"><span style="color:#b48ead;">class </span><span style="color:#ebcb8b;">Acme::RegistrationsController </span><span style="color:#eff1f5;">< </span><span style="color:#a3be8c;">Devise::RegistrationsController
</span><span> before_filter </span><span style="color:#a3be8c;">:configure_account_update_params</span><span>, </span><span style="color:#a3be8c;">only: </span><span>[</span><span style="color:#a3be8c;">:update</span><span>]
</span><span>
</span><span> </span><span style="color:#b48ead;">def </span><span style="color:#8fa1b3;">configure_account_update_params
</span><span> devise_parameter_sanitizer.for(</span><span style="color:#a3be8c;">:account_update</span><span>) << </span><span style="color:#a3be8c;">:mobile_number
</span><span> </span><span style="color:#b48ead;">end
</span><span style="color:#b48ead;">end
</span></code></pre>
<p>Note that this code should already exists and is commented out. All we've done is change <code>:attribute</code> to <code>:mobile_number</code>.</p>
<p>At this point a user can add a phone number and we can verify it's a valid number. To go one step further we need to send the user a validation code for them to enter and verify that they are in possession of the phone.</p>
<h2 id="sending-a-verification-code-to-their-mobile">Sending a verification code to their mobile</h2>
<p>First let's make a helper method in the <code>User</code> model which will tell us if the account needs to be verified</p>
<pre data-lang="ruby" style="background-color:#2b303b;color:#c0c5ce;" class="language-ruby "><code class="language-ruby" data-lang="ruby"><span style="color:#65737e;"># app/models/user.rb
</span><span style="color:#65737e;"># ...
</span><span> </span><span style="color:#b48ead;">def </span><span style="color:#8fa1b3;">needs_mobile_number_verifying?
</span><span> </span><span style="color:#b48ead;">if</span><span> is_verified
</span><span> </span><span style="color:#b48ead;">return </span><span style="color:#d08770;">false
</span><span> </span><span style="color:#b48ead;">end
</span><span> </span><span style="color:#b48ead;">if</span><span> mobile_number.empty?
</span><span> </span><span style="color:#b48ead;">return </span><span style="color:#d08770;">false
</span><span> </span><span style="color:#b48ead;">end
</span><span> </span><span style="color:#b48ead;">return </span><span style="color:#d08770;">true
</span><span> </span><span style="color:#b48ead;">end
</span><span style="color:#65737e;"># ...
</span></code></pre>
<p>If the user is already verified it will return false and if there is no mobile number to verify it will also return false. We'll be using this method shortly.</p>
<p>Create a new controller for handling mobile number verifications. I called mine <code>VerificationsController</code></p>
<pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>rails g controller verifications
</span></code></pre>
<p>Add a route in your routes for the <code>create</code> method.</p>
<pre data-lang="ruby" style="background-color:#2b303b;color:#c0c5ce;" class="language-ruby "><code class="language-ruby" data-lang="ruby"><span style="color:#65737e;"># config/routes.rb
</span><span>post '</span><span style="color:#a3be8c;">verifications</span><span>' => '</span><span style="color:#a3be8c;">verifications#create</span><span>'
</span></code></pre>
<p>If you're using <code>dotenv</code> then now is the time to create a <code>.env</code> file in the root of your project and add your test credentials.</p>
<pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>TWILIO_SID=""
</span><span>TWILIO_TOKEN=""
</span><span>TWILIO_PHONE_NUMBER="+15005550006"
</span></code></pre>
<p>Bear in mind that <code>TWILIO_PHONE_NUMBER</code> is currently set to the test number which is for sending valid text messages. Once you go live you will need to change it your own Twilio number. You will need to get your own SID and token from your Twilio account.</p>
<p>Restart your rails server after updating your environment variables.</p>
<p>In the new controller add the following code</p>
<pre data-lang="ruby" style="background-color:#2b303b;color:#c0c5ce;" class="language-ruby "><code class="language-ruby" data-lang="ruby"><span>
</span><span style="color:#65737e;"># ...
</span><span style="color:#b48ead;">def </span><span style="color:#8fa1b3;">create
</span><span> current_user.verification_code = </span><span style="color:#d08770;">1_000_000 </span><span>+ </span><span style="color:#96b5b4;">rand</span><span>(</span><span style="color:#d08770;">10_000_000 </span><span>- </span><span style="color:#d08770;">1_000_000</span><span>)
</span><span> current_user.save
</span><span>
</span><span> to = current_user.mobile_number
</span><span> </span><span style="color:#b48ead;">if</span><span> to[</span><span style="color:#d08770;">0</span><span>] = "</span><span style="color:#a3be8c;">0</span><span>"
</span><span> to.</span><span style="color:#96b5b4;">sub!</span><span>("</span><span style="color:#a3be8c;">0</span><span>", '</span><span style="color:#a3be8c;">+44</span><span>')
</span><span> </span><span style="color:#b48ead;">end
</span><span>
</span><span> @</span><span style="color:#bf616a;">twilio_client </span><span>= </span><span style="color:#ebcb8b;">Twilio</span><span>::</span><span style="color:#ebcb8b;">REST</span><span>::</span><span style="color:#ebcb8b;">Client</span><span>.</span><span style="color:#8fa1b3;">new </span><span style="color:#bf616a;">ENV</span><span>['</span><span style="color:#a3be8c;">TWILIO_SID</span><span>'], </span><span style="color:#bf616a;">ENV</span><span>['</span><span style="color:#a3be8c;">TWILIO_TOKEN</span><span>']
</span><span> @</span><span style="color:#bf616a;">twilio_client</span><span>.account.sms.messages.create(
</span><span> </span><span style="color:#a3be8c;">:from </span><span>=> </span><span style="color:#bf616a;">ENV</span><span>['</span><span style="color:#a3be8c;">TWILIO_PHONE_NUMBER</span><span>'],
</span><span> </span><span style="color:#a3be8c;">:to </span><span>=> to,
</span><span> </span><span style="color:#a3be8c;">:body </span><span>=> "</span><span style="color:#a3be8c;">Your verification code is </span><span>#{current_user.verification_code}</span><span style="color:#a3be8c;">.</span><span>"
</span><span> )
</span><span> redirect_to edit_user_registration, </span><span style="color:#a3be8c;">:flash </span><span>=> { </span><span style="color:#a3be8c;">:success </span><span>=> "</span><span style="color:#a3be8c;">A verification code has been sent to your mobile. Please fill it in below.</span><span>" }
</span><span> </span><span style="color:#b48ead;">return
</span><span style="color:#b48ead;">end
</span><span style="color:#65737e;"># ...
</span></code></pre>
<ul>
<li>
<p>On line 3 we create a random six digit verification code and assign it to the user.</p>
</li>
<li>
<p>Lines 6-9 checks if the number starts with 0 and if it does then convert it to +44 (which is the UK dialing code). Alternatively you may wish to save this to the database instead of doing it for every text.</p>
</li>
<li>
<p>On line 11 we create an instance of the Twilio class with our credentials.</p>
</li>
<li>
<p>Lines 12-16 handles sending the message to Twilio.</p>
</li>
<li>
<p>On line 17 we redirect back to the edit profile page.</p>
</li>
</ul>
<p>We're going to add a "Verify mobile number" button to the edit page but there's no point in showing it if the user already has no mobile number or is already verified. This is where the <code>needs_mobile_number_verifying?</code> method from earlier comes in to play. We'll add a helper method to store the logic for showing the form.</p>
<pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>rails g helper acme/registrations
</span></code></pre>
<pre data-lang="ruby" style="background-color:#2b303b;color:#c0c5ce;" class="language-ruby "><code class="language-ruby" data-lang="ruby"><span style="color:#65737e;"># app/helpers/acme/registrations_helper.rb
</span><span style="color:#b48ead;">module </span><span>Registrations::RegistrationsHelper
</span><span> </span><span style="color:#b48ead;">def </span><span style="color:#8fa1b3;">mobile_verification_button
</span><span> </span><span style="color:#b48ead;">return </span><span>'' </span><span style="color:#b48ead;">unless</span><span> current_user.needs_mobile_number_verifying?
</span><span> html = <<-HTML
</span><span> <</span><span style="color:#bf616a;">h3</span><span>>Verify Mobile Number</</span><span style="color:#bf616a;">h3</span><span>>
</span><span> #{form_tag(verifications_path, </span><span style="color:#a3be8c;">method: </span><span>"</span><span style="color:#a3be8c;">post</span><span>")}
</span><span> #{button_tag('</span><span style="color:#a3be8c;">Send verification code</span><span>', </span><span style="color:#a3be8c;">type: </span><span>"</span><span style="color:#a3be8c;">submit</span><span>")}
</span><span> </</span><span style="color:#bf616a;">form</span><span>>
</span><span style="color:#a3be8c;"> </span><span>HTML
</span><span> html.html_safe
</span><span> </span><span style="color:#b48ead;">end
</span><span style="color:#b48ead;">end
</span></code></pre>
<p>On line 4 we return an empty string unless the user needs to be verified. On lines 5-11 we have a heredoc which contains the new form. Note that we could use plain HTML here but the <code>form_tag</code> allows us to use a path and it also adds the CSRF token for us. Lastly, we return ~~~html.html_safe``` which will be the output when we use it in our view.</p>
<p>Now in <code>app/views/devise/registrations/edit.html.erb</code> add the following code after the main form.</p>
<pre data-lang="ruby" style="background-color:#2b303b;color:#c0c5ce;" class="language-ruby "><code class="language-ruby" data-lang="ruby"><span style="color:#65737e;"># ...
</span><span><%= mobile_verification_button %>
</span><span style="color:#a3be8c;"><h3</span><span>></span><span style="color:#bf616a;">Cancel</span><span> my account</h3>
</span><span style="color:#65737e;"># ...
</span></code></pre>
<p>Now this form will only show when <code>is_verified</code> is <code>false</code> and <code>mobile_number</code> is not empty.</p>
<p>At this point you should enter a mobile number and click the "Send verification code" button. </p>
<p>With test credentials you won't receive a text but if you spin up <code>rails console</code> and type <code>User.first</code> you should see a 6 digit number in <code>verification_code</code> field. This code will change each time you click the button.</p>
<h2 id="entering-the-code-for-verification">Entering the code for verification</h2>
<p>Add a new route and controller method to handle verification</p>
<pre data-lang="ruby" style="background-color:#2b303b;color:#c0c5ce;" class="language-ruby "><code class="language-ruby" data-lang="ruby"><span style="color:#65737e;"># config/routes.rb
</span><span>put '</span><span style="color:#a3be8c;">verifications</span><span>' => '</span><span style="color:#a3be8c;">verifications#verify</span><span>'
</span></code></pre>
<pre data-lang="ruby" style="background-color:#2b303b;color:#c0c5ce;" class="language-ruby "><code class="language-ruby" data-lang="ruby"><span style="color:#65737e;"># app/controllers/verifications_controller.rb
</span><span style="color:#b48ead;">def </span><span style="color:#8fa1b3;">verify
</span><span> </span><span style="color:#b48ead;">if</span><span> current_user.verification_code == params[</span><span style="color:#a3be8c;">:verification_code</span><span>]
</span><span> current_user.is_verified = </span><span style="color:#d08770;">true
</span><span> current_user.verification_code = ''
</span><span> current_user.save
</span><span> redirect_to edit_user_registration_path, </span><span style="color:#a3be8c;">:flash </span><span>=> { </span><span style="color:#a3be8c;">:success </span><span>=> "</span><span style="color:#a3be8c;">Thank you for verifying your mobile number.</span><span>" }
</span><span> </span><span style="color:#b48ead;">return
</span><span> </span><span style="color:#b48ead;">else
</span><span> redirect_to edit_user_registration_path, </span><span style="color:#a3be8c;">:flash </span><span>=> { </span><span style="color:#a3be8c;">:errors </span><span>=> "</span><span style="color:#a3be8c;">Invalid verification code.</span><span>" }
</span><span> </span><span style="color:#b48ead;">return
</span><span> </span><span style="color:#b48ead;">end
</span><span style="color:#b48ead;">end
</span></code></pre>
<p>Let's make another helper method which will work the same way as the previous one.</p>
<p>This form will display when the <code>verification_code</code> field isn't empty so the user can enter their code.</p>
<pre data-lang="ruby" style="background-color:#2b303b;color:#c0c5ce;" class="language-ruby "><code class="language-ruby" data-lang="ruby"><span style="color:#65737e;"># app/helpers/acme/registrations_helper.rb
</span><span> </span><span style="color:#b48ead;">def </span><span style="color:#8fa1b3;">verify_mobile_number_form
</span><span> </span><span style="color:#b48ead;">return </span><span>'' </span><span style="color:#b48ead;">if</span><span> current_user.verification_code.empty?
</span><span> </span><span style="color:#96b5b4;">p</span><span> current_user.verification_code.empty?
</span><span> html = <<-HTML
</span><span> <</span><span style="color:#bf616a;">h3</span><span>>Enter Verification Code</</span><span style="color:#bf616a;">h3</span><span>>
</span><span> #{form_tag(verifications_path, </span><span style="color:#a3be8c;">method: </span><span>"</span><span style="color:#a3be8c;">patch</span><span>")}
</span><span> #{text_field_tag('</span><span style="color:#a3be8c;">verification_code</span><span>')}
</span><span> #{button_tag('</span><span style="color:#a3be8c;">Submit</span><span>', </span><span style="color:#a3be8c;">type: </span><span>"</span><span style="color:#a3be8c;">submit</span><span>")}
</span><span> </</span><span style="color:#bf616a;">form</span><span>>
</span><span style="color:#a3be8c;"> </span><span>HTML
</span><span> html.html_safe
</span><span> </span><span style="color:#b48ead;">end
</span></code></pre>
<p>Finally, add the helper to <code>app/views/devise/registrations/edit.html.erb</code>. </p>
<pre data-lang="html" style="background-color:#2b303b;color:#c0c5ce;" class="language-html "><code class="language-html" data-lang="html"><span><%= verify_mobile_number_form %>
</span></code></pre>
<p>You can check it's working by generating the verification code and using <code>rails console</code> <code>User.first</code> to retrieve and enter the correct code.</p>
<p>Once you're happy that it works we should let the user know if their mobile is verified in their profile</p>
<pre data-lang="html" style="background-color:#2b303b;color:#c0c5ce;" class="language-html "><code class="language-html" data-lang="html"><span> <</span><span style="color:#bf616a;">div </span><span style="color:#d08770;">class</span><span>="</span><span style="color:#a3be8c;">field</span><span>">
</span><span> <%= f.label :mobile_number %><</span><span style="color:#bf616a;">br </span><span>/>
</span><span> <%= f.text_field :mobile_number %>
</span><span> <% if resource.is_verified %>
</span><span> You're mobile number is verified.
</span><span> <% end%>
</span><span> </</span><span style="color:#bf616a;">div</span><span>>
</span></code></pre>
<p>It's also a good idea to stop the code from showing in the log files.</p>
<pre data-lang="ruby" style="background-color:#2b303b;color:#c0c5ce;" class="language-ruby "><code class="language-ruby" data-lang="ruby"><span style="color:#65737e;"># app/models/user.rb
</span><span>filter_parameter_logging </span><span style="color:#a3be8c;">:verification_code
</span></code></pre>
<h2 id="final-notes">Final notes</h2>
<p>You have a basis for the system here but it can be improved. </p>
<p>If a user enters a new mobile number <code>is_verified</code> should be changed to <code>false</code>.</p>
<p>If <code>rand</code> isn't secure enough for your verification code generator then take a look at <a href="https://github.com/mdp/rotp">The Ruby One Time Password Library</a>.</p>
<p>The current set up is only ideal for users that have one mobile number. What happens if they want to enter more than one?</p>
<p>Lastly, as with emails, sending text messages can be slow. Not waiting-for-a-video-to-load-on-dial-up slow but enough for me to recommend setting up ActiveJob. I've been having some good success with Resque for this task.</p>
<p>Here's a quick snippet of the code I've used on a recent project.</p>
<pre data-lang="ruby" style="background-color:#2b303b;color:#c0c5ce;" class="language-ruby "><code class="language-ruby" data-lang="ruby"><span style="color:#65737e;"># controller
</span><span style="color:#ebcb8b;">SendVerificationCodeJob</span><span>.perform_later(user)
</span><span>
</span><span style="color:#65737e;"># app/jobs/send_verification_code_job.rb
</span><span style="color:#b48ead;">class </span><span style="color:#ebcb8b;">SendVerificationCodeJob </span><span style="color:#eff1f5;">< </span><span style="color:#a3be8c;">ActiveJob::Base
</span><span> queue_as </span><span style="color:#a3be8c;">:default
</span><span>
</span><span> </span><span style="color:#b48ead;">def </span><span style="color:#8fa1b3;">perform</span><span>(</span><span style="color:#bf616a;">user</span><span>)
</span><span> </span><span style="color:#65737e;"># generate verification code
</span><span>
</span><span> user.verification_code = </span><span style="color:#d08770;">100_000 </span><span>+ </span><span style="color:#96b5b4;">rand</span><span>(</span><span style="color:#d08770;">1_000_000 </span><span>- </span><span style="color:#d08770;">100_000</span><span>)
</span><span>
</span><span> user.save
</span><span> to = user.mobile_number
</span><span> </span><span style="color:#b48ead;">if</span><span> to[</span><span style="color:#d08770;">0</span><span>] = "</span><span style="color:#a3be8c;">0</span><span>"
</span><span> to.</span><span style="color:#96b5b4;">sub!</span><span>("</span><span style="color:#a3be8c;">0</span><span>", '</span><span style="color:#a3be8c;">+44</span><span>')
</span><span> </span><span style="color:#b48ead;">end
</span><span>
</span><span> </span><span style="color:#65737e;"># twilio send
</span><span> @</span><span style="color:#bf616a;">twilio_client </span><span>= </span><span style="color:#ebcb8b;">Twilio</span><span>::</span><span style="color:#ebcb8b;">REST</span><span>::</span><span style="color:#ebcb8b;">Client</span><span>.</span><span style="color:#8fa1b3;">new </span><span style="color:#bf616a;">ENV</span><span>['</span><span style="color:#a3be8c;">TWILIO_SID</span><span>'], </span><span style="color:#bf616a;">ENV</span><span>['</span><span style="color:#a3be8c;">TWILIO_TOKEN</span><span>']
</span><span>
</span><span> @</span><span style="color:#bf616a;">twilio_client</span><span>.account.sms.messages.create(
</span><span> </span><span style="color:#a3be8c;">:from </span><span>=> </span><span style="color:#bf616a;">ENV</span><span>['</span><span style="color:#a3be8c;">TWILIO_PHONE_NUMBER</span><span>'],
</span><span> </span><span style="color:#a3be8c;">:to </span><span>=> to,
</span><span> </span><span style="color:#a3be8c;">:body </span><span>=> "</span><span style="color:#a3be8c;">Your verification code is </span><span>#{user.verification_code}</span><span style="color:#a3be8c;">.</span><span>"
</span><span> )
</span><span> </span><span style="color:#b48ead;">end
</span><span style="color:#b48ead;">end
</span></code></pre>