Verificación de los números telefónicos en Ruby on Rails con la API de Verify de Twilio

September 07, 2018
Redactado por
Daniel Phillips
Colaborador
Las opiniones expresadas por los colaboradores de Twilio son propias

Verificación de los números telefónicos en Ruby on Rails con la API de Verify de Twilio

En el mundo de las aplicaciones que interactúan con el público, verificar que los usuarios son personas reales puede ser difícil. Aquí es donde la verificación telefónica realmente se convierte en un activo que ayuda a mitigar el fraude.

En esta publicación, analizaremos la integración de la API de Verify de Twilio en la aplicación Ruby on Rails para descubrir si existe el número de teléfono de un usuario, cuál es su tipo de línea y cuál es su operador. Luego, autenticaremos al usuario con el token de verificación de Verify.  

Cómo empezar

Vamos a crear una simple acción de inicio de sesión con verificación telefónica. Este tutorial proporcionará funcionalidad básica que se puede trasladar a aplicaciones Ruby/Rails nuevas o existentes.

En un nivel alto, las interacciones exitosas tendrán un aspecto similar al siguiente:

  1. El usuario ingresa un número de teléfono a la aplicación.
  2. Se verifica si el número de teléfono es real o no, y si el teléfono es un teléfono móvil o una línea fija.
  3. Si el número es una celda válida, se envía al usuario un código de autenticación.
  4. El usuario ingresa el código para completar el proceso de autenticación.

Por supuesto que muchas cosas pueden salir mal en este proceso, así que tendremos que planear el manejo de excepciones a este proceso a medida que construimos este servicio.

Nota: el código completo para este tutorial está disponible aquí.

Configuración del entorno

Utilizaremos Ruby 2.5.0, mejor instalada y administrada usando ya sea rvmobenv, y utilizaremos PostgreSQL para su almacenamiento. Si está en una Mac, le recomiendo utilizar homebrew para instalarlas; de lo contrario, consulte las instrucciones de instalación para su entorno en la documentación de cada servicio. También necesitarás instalar la gema de Rails; puede hacerlo con:

gem install rails

que también nos proporciona Bundler, que usaremos para la administración de paquetes.

Luego, inicie una nueva aplicación en Rails con:

rails new bot_or_not -d postgresql
cd bot_or_not

Para este proyecto, tendremos que instalar la gema Authy. Esta biblioteca nos permite interactuar con la API de Twilio Verify.

Por lo tanto, en su Gemfile, agregue lo siguiente:

gem 'authy'

Agreguemos también Pry a nuestro Gemfile para una excelente depuración de Ruby:

En el bloque de group:development (¡no queremos accidentalmente un depurador en prod!), agregue

gem 'pry-rails' 

Luego, en la línea de comandos, ejecute:

bundle install

Por último, vamos a crear nuestras bases de datos de desarrollo y de prueba:

rails db:create

Creación del servicio de inicio de sesión        

Para tener una página de inicio de sesión en funcionamiento, necesitamos implementar la capacidad de CRUD. un recurso, que en nuestro caso, es un usuario.

Podríamos desarrollar esta capacidad a mano, pero, para los fines de este artículo, ¿por qué no aprovechar la capacidad predeterminada que tiene Rails para hacer esto por nosotros? Los generadores de Rails pueden crear todo un recurso CRUD para nosotros, cumpliendo con el enrutamiento RESTful y el diseño MVC(muchas palabras de moda aquí, no dude en seguir los enlaces para obtener más información :) ).

Desde la línea de comandos:

bash rails generate scaffold User name:text country_code:string phone_number:text

Si no estás familiarizado con Rails, solo debes saber que este hace mucho por nosotros. En primer lugar, crea una migración para hacer una tabla de users con columnas de name y phone_number. Luego, crea un modelo de usuario correspondiente. Hace todas las rutas RESTful para el recurso de usuario, acciones de controlador para cada ruta y, por último, vistas muy simples de las acciones en Rails para el recurso de usuario. Para verificarlo, intente lo siguiente:

rake routes

Y debe ver esto:

Prefix       Verb       URI Pattern                  Controller#Action
users        GET        /users(.:format)             users#index
             POST       /users(.:format)             users#create
new_user     GET        /users/new(.:format)         users#new
edit_user    GET        /users/:id/edit(.:format)    users#edit
user         GET        /users/:id(.:format)         users#show
             PATCH      /users/:id(.:format)         users#update
             PUT        /users/:id(.:format)         users#update
             DELETE     /users/:id(.:format)         users#destroy

Nota: si utiliza rails 5.2, es posible que vea más rutas relacionadas con Active Storage, pero no tenemos que preocuparnos de ellas ahora para nuestros fines.

Entonces, ejecutamos nuestra migración para crear nuestra tabla de usuarios y activar nuestro servidor de desarrollo:

rails db:migrate && rails s

Según las rutas que vemos arriba, si abrimos un navegador y navegamos al host localhost:3000/users/new, debemos ver lo siguiente:

pantalla del navegador con el formulario que contiene el nombre, código de país, número de teléfono y un botón para crear un usuario y un enlace para el retorno

¡Magia de Rails!

Continue, cree un usuario y acepte. Después de hacerlo, debe ver:

Tela do navegador com a mensagem dizendo que o usuário foi criado com sucesso

Fantástico. Ahora sabemos que podemos crear y guardar usuarios y números de teléfono.

Registrar una app en la consola de Twilio

Necesitamos algunas credenciales para interactuar con la API de Authy. Para acceder a la consola de desarrollador de Twilio, diríjase a https://www.twilio.com/console/verify/applications. Si no tiene una cuenta, no dude en registrarse con una cuenta gratuita, que será suficiente para lo que estamos creando aquí.

Una vez que haya iniciado sesión, seleccione “Create New Application” (Crear nueva aplicación) para comenzar. Después de darle un nombre a la app, debería ver lo siguiente:

Pantalla de la consola Twilio con el producto Verify seleccionado y su configuración general

Los detalles de esta página ayudan a configurar exactamente cómo los usuarios interactúan con la aplicación. Por ahora, lo que realmente necesitamos es la clave de API para hacer funcionar algunas solicitudes. Tome esa clave de API:

Pantalla de consola parcial de Twilio con información de clave API

Copie la clave en el portapapeles y vuelva a la consola.

Intente lo siguiente:

curl -XPOST 'https://api.authy.com/protected/json/phones/verification/start' -H "X-Authy-API-Key: <YOUR_KEY>" -d via='sms' -d phone_number=<VALID_CELL_NUMBER> -d country_code=<VALID_COUNTRY_CODE>

Asegúrese de reemplazar <YOUR_KEY> por la API copiada de la consola. Con respecto a <VALID_CELL_NUMBER>, recomiendo utilizar su propio número de teléfono celular para que no haya extraños que reciban invitaciones para autenticarse en su app.

¡Y listo! Debería haber recibido un objeto en respuesta, incluida la información sobre el teléfono celular y las acciones realizadas. Además, con el uso de la celda, debe recibir un mensaje de texto.

Usar Authy-Ruby

Ahora podemos realizar solicitudes exitosas de información acerca de nuestros números de teléfono celular y su validez. El siguiente paso es hacer esto mediante programación en nuestra app.

Nuestra gema de Authy nos da acceso a una biblioteca Ruby que resume la API de Authy de Twilio. Tiene un poco de funcionalidad incorporada, pero estamos especialmente interesados en la clase de PhoneVerification,con sus métodos de start y de check.

De acuerdo con los documentos, start empieza el proceso verificando la información de un teléfono celular de un usuario y que tenga la capacidad de SMS, y al recibir esas dos cosas, la API envía un mensaje de texto al usuario con un código con vencimiento. Probemos esto en la consola de Rails:

rails c

Aquí, podemos seguir adelante y probar la biblioteca de Authy:

pry(main)> Authy::PhoneVerification.start(country_code: <YOUR_COUNTRY_CODE>, phone_number: '<YOUR_CELL>')

=> {"error_code"=>"60001", "message"=>"Invalid API key", "errors"=>{"message"=>"Invalid API key"}, "success"=>false}

¡Vaya! La biblioteca no conoce la clave de API que se debe utilizar. En su directorio config/initializers, agregue el archivo authy.rb, y en él:

Authy.api_key = 'your-api-key'
Authy.api_url  = 'https://api.authy.com'

Nota: para mantener privadas las claves de la API, debe agregar este archivo a su archivo .gitignore si tiene previsto hacerlo para proyectar en un repositorio público.

Ahora reinicie la consola de Rails y vuelva a intentarlo. Con un número de teléfono válido, debería ver una respuesta como:

pry(main)> Authy::PhoneVerification.start(country_code: <YOUR_COUNTRY_CODE>, phone_number: '<YOUR_CELL>')
=> {"carrier"=>"Verizon Wireless", "is_cellphone"=>true, "message"=>"Text message sent to +1 XXX-XXX-XXXX.", "seconds_to_expire"=>599, "uuid"=>"xxxxxxxxxxxxxxxxx", "success"=>true}

Además, el número de teléfono que proporcionó debería haber recibido algo como lo siguiente:

Mensaje SMS recibido con código de verificación.

El código proporcionado es válido durante 10 minutos, por lo que si aún se encuentra dentro de ese período, podemos intentar el método de comprobación con el mismo código (no se preocupe si ha vencido, solo debería poder enviar el método de inicio nuevamente):

Authy::PhoneVerification.check(verification_code: '<CODE>', country_code: '<YOUR_COUNTRY_CODE>' , phone_number: '<SAME_NUMBER_FROM_FIRST_REQUEST>')

=> {"message"=>"Verification code is correct.", "success"=>true}

Genial, hemos añadido la función de verificación mediante la biblioteca. Ahora obtengamos esta funcionalidad en nuestra app.

Para mantener las responsabilidades separadas, agreguemos un nuevo directorio /services/ en el directorio ./app/, y en él, un archivo verify.rb. Debido a que la naturaleza de este servicio será funcional y no tendrá que manejar el estado, hagamos de este un módulo Ruby.

module Verify
end 

En él, comencemos con un método para determinar si el número de teléfono es válido, como lo hicimos en nuestra consola:

module Verify
  def valid_phone_number?(country_code, phone_number)
    response = Authy::PhoneVerification.start(country_code: country_code, phone_number: phone_number)
    response.success?
  end
end

De esta manera, el método actuará como protección, al entregar un booleano que podemos utilizar fácilmente para controlar el flujo a nuestro user_controller. Para tener el método disponible en nuestro controlador, asegúrese de include a este en nuestra clase de UserController.

class UsersController < ApplicationController
  include Verify

  # rest of the controller
end

En la parte inferior del archivo, el andamiaje nos dio un método privado llamado user_params.

    def user_params
      params.require(:user).permit(:name, :country_code, :phone_number)
    end

Cuando se activa, este método crea un hash de solo aquellos atributos que se pasan al método de permiso. Esto será útil cuando necesitemos utilizar el country_code and phone_number ingresados para la verificación.

Notará que apareciendo muchas cosas cuando andamiamos el recurso del usuario, algunos de los cuales no necesitamos. Cuando pensamos en nuestras interacciones de alto nivel, aquí podemos abordar las primeros dos:

  • El usuario envía un número de teléfono a la aplicación.
  • El número de teléfono se verifica como real o no; se determina el tipo de línea telefónica.

Trabajemos un poco para nuestro método de creación. Deshágase del código de andamiaje que apareció en el método de creación y reemplácelo con:

def create
@user = User.new(user_params)
    if valid_phone_number?(user_params['country_code'], user_params['phone_number'])
      @user.save
      redirect_to @user, notice: 'You have a valid phone number!'
    else
      flash.alert = 'Please enter a valid phone number'
      render :new
    end
end

Tenga que cuenta que utilizamos una alerta para el otro estado. Para asegurarse de que esta muestra se realice correctamente, vaya a /app/view/users/new.html.erb y cambie de:

<p id="notice"><%= notice %></p>

a:  

<p id="alert"><%= alert %></p>

Las adiciones y los cambios anteriores harán algunas cosas por nosotros: la declaración inicial evaluará nuestro valid_phone_number? método como verdadero o falso. Si se realiza correctamente, le indicaremos el nombre de usuario y el número de teléfono a nuestra tabla de los usuarios; de lo contrario, con un número no verificado, se ejecutará el estado else y el usuario será redirigido nuevamente al formulario inicial.

¡Inténtelo!

Gire nuestro servidor local de Rails con rails s y vuelva a localhost:3000/users/new.

Debe ver el mismo formulario, pero ahora debe tener un comportamiento diferente. Ingresar un número sin sentido hará que el método de guardia que hemos agregado anteriormente nos devuelva un falso, lo que nos llevará a este mismo formulario. Al ingresar un número válido debería permitirnos crear el usuario y enviar un SMS como vimos anteriormente.

Aún necesitamos implementar nuestro método de check. Esto debería suceder _solamente después_ de guardar un usuario con un número de teléfono válido, ya que un código válido solo se puede enviar por mensaje de texto a un usuario con un número válido.

Podemos lidiar con esto agregando una entrada a la ruta usuario#show. Este enfoque se ajusta bien a nuestro flujo de aplicaciones, ya que solo se envía a un usuario a este recurso si se crea, y solo se crea si el usuario proporciona un número de celular válido.

En nuestras vistas, a continuación, agregue un simple Rails form_tag al usuario/show.html.erb.

<h3>Verify your number</h3>
<%= form_tag @user, url: user_path(@user.id), :method => :put do %>
  <%= label_tag :code, "Enter the code you received at #{@user.phone_number} here" %>
  <%= text_field_tag :code %>
  <%= submit_tag 'Verfiy' %>
<% end %>

Que, en el contexto de lo que ya estaba ahí desde el andamiaje, debe tener un aspecto similar al siguiente:

Pantalla del navegador con campo para completar el código recibido

Notará en nuestro form_tag que especificamos el verbo y el recurso, por lo que esta acción nos llevará a la acción de actualización en el controlador del usuario.

En este punto, necesitamos agregar a nuestro módulo un método de “check”. La interfaz para este método tendrá que identificar el número de teléfono del usuario y comprobar el código que recibió el usuario con respecto a este. Una vez más, esta es una buena oportunidad para un método de protección que devuelva un booleano. Por lo tanto, en el módulo de Verify, agregue lo siguiente:

  def valid_confirmation_code?(code, country_code, phone_number)
    response = Authy::PhoneVerification.check(verification_code: code, country_code: country_code, phone_number: phone_number)
    response.success?
  end

Ahora podemos comprobar el código con la información local de usuario persistente y compararlo con la API de Authy para completar la verificación. Además, es posible que queramos indicar en nuestra actividad en segundo plano que el usuario se ha verificado. Es lo suficientemente fácil como para agregarlo a la tabla del usuario:

rails g migration AddVerifiedToUser

Y en el archivo de migración generado:

  def change
    add_column :users, :verified, :boolean, default: false
  end

Luego, vuelva a la línea de comandos

rails db:migrate

Con esto en su lugar, agreguemos un indicador en la página que muestra a los usuarios, junto con un solo protector que muestre el formulario si el usuario no está verificado.

<p>
<strong>Verified:</strong>
<%= @user.verified %>
</p>

<% if !@user.verified %>
  <h3>Verify your number</h3>
  <%= form_tag @user, url: user_path(@user.id), :method => :put do %>
  <%= label_tag :code, "Enter the code you received at #{@user.phone_number} here" %>
  <%= text_field_tag :code %>
  <%= submit_tag 'Verify' %>
  <% end %>
<% end %>

Por lo tanto, si se cambia esta funcionalidad ahora a nuestro controlador, tómese un minuto para reorganizar la acción de actualización. En primer lugar, dado que fundamentalmente estamos cambiando la acción de actualización, no queremos permitir la capacidad de edición que nos dio nuestro andamiaje. Entonces, deshágase de cualquier cosa que llevara de vuelta a la acción de actualización, excepto del flujo de verificación telefónica:

En /app/views/users/show.html.erb y en /app/views/users/index.html.erb, elimine

<%= link_to 'Edit', edit_user_path(@user) %>

Luego, en nuestra acción de actualización:

  def update
    if  valid_confirmation_code?(params['code'], @user.country_code, @user.phone_number)
      @user.update(verified: true)
      redirect_to users_path, notice: "#{@user.phone_number} has been verified!"
    else
      redirect_to @user, alert: 'invalid or expired token'
    end
  end

Aquí, hemos manejado la acción de actualización de manera similar a la acción de creación en términos de control de flujo. La diferencia clave es que, cuando se realiza correctamente la devolución del código de confirmación válido, actualizamos la columna de verificación en el usuario.

Para garantizar que nuestro aviso satisfactorio o alerta de error se muestre en la página, debemos agregar una cosa más a la vista. Abra app/views/users/show.html.erb y agregue el mensaje de alerta debajo del aviso.

<p id="notice"><%= notice %></p>
<p id="alert"><%= alert %></p>

Y con esto, hemos manejado toda la lógica de nuestro proceso de autenticación.  

Próximos pasos

Esta es una implementación sencilla de Verify para los números de teléfono. Para profundizar un poco más, le sugeriría que analice otros métodos de verificación que ofrece el Authy gem y que pruebe con las otras herramientas de Twilio para prevenir el fraude, específicamente la Lookup API.

Este artículo fue traducido del original "Verifying Phone Numbers in Ruby on Rails with Twilio's Verify API". Mientras estamos en nuestros procesos de traducción, nos encantaría recibir sus comentarios en help@twilio.com - las contribuciones valiosas pueden generar regalos de Twilio.