Desafío Latam
Uncategorized

Login con Facebook y Twitter para tu proyecto Rails

skills omniauth 92c5732e4fd628e46873a31c86b4058d

skills omniauth 92c5732e4fd628e46873a31c86b4058d

Uno de los requerimientos más solicitados hoy en día por nuestros clientes a la hora de desarrollar una aplicación web es la integración con redes sociales, o también, es un requisito casi esencial para tu proyecto de emprendimiento, es por ello que en esta guía ahondaremos en los pasos necesarios para llevar este proceso a cabo, haciendo uso de las APIs públicas de Facebook y Twitter en particular, ya que son las redes sociales más utilizadas en este minuto.

Creando tu App en el Portal de Desarrollo

Primero lo primero, crearemos y solicitaremos los permisos necesarios para tener nuestra app funcionando en las plataformas para de esta forma obtener nuestros códigos de Cliente y Secreto.

Facebook

Para ello, accedemos a la sección de desarrolladores de Facebook (https://developers.facebook.com).

           

En el menú superior encontrarán “My Apps” y dentro de el menú que se despliega está la opción “Add a New App”, y les aparecerán las siguientes opciones.

Y bueno, estamos configurando una app web, así que nos vamos con toda seguridad a la última opción de la derecha “Sitio web”. Seguido de esto, les pedirá crear un nombre para su app, esto se los dejo a su creatividad, luego presionamos en el botón inferior “Create New Facebook App ID”.

Seguidamente, elegimos una categoría y nos vamos a “Create App ID”. (Ignoren la parte de “Is this a test version of…”, déjenlo en NO).

En este minuto, Facebook les debería estar entregando un tutorial de inicio, no lo veo necesario, pero si quieren revisarlo, está ahí para ustedes. Seguimos presionando en el botón superior “Skip Quick Start”.

¡Ahora estamos en lo que necesitamos para preconfigurar nuestro futuro proyecto Rails! Copien su App ID y su App Secret (les pedirá colocar su contraseña para mostrarlo luego de presionar en el botón “Show”.

Otro dato interesante es que Facebook por defecto les da permisos sobre los usuarios que accedan a la app creada, pueden revisarlos en “Status & Review”.

¡Y ya estamos listos con Facebook!

Twitter

Para nuestra querida red social del pajarito celeste, el procedimiento no es tan diferente que el anterior, así que comencemos accediendo a https://apps.twitter.com.

Como era de esperarse, presionamos en “Create New App” que nos mostrará el siguiente formulario, el cual rellenaremos según lo que vayas a desarrollar, los campos Website y Callback URL los rellenaremos con los campos que aparecerán en la imagen, de esta forma, podemos probar el login desde nuestro propio PC.

Aceptamos los Términos y Condiciones de Uso y presionamos en “Create Your Twitter Application”, luego, nos presentará la siguiente vista:

Y estamos casi a punto de obtener todo lo que necesitamos para empezar a programar, pero primero, vamos al a sección “Keys and Access Tokens”:

Y copiamos los dos códigos que necesitaremos, Consumer Key y Consumer Secret. Luego de esto empezaremos a entrar a desarrollar tu app e integrarla con estas dos redes sociales.

Instalando Devise

Instalar esta gema es un paso bastante mecánico, pero fácil de realizar. Primero que nada incluiremos la gema en nuestro Gemfile:

gem "devise"

Como ya han de estar acostumbrados, realizamos:

bundle install

Con estos dos pasos ya tendrían instalada esta gema, ahora sólo nos queda configurar nuestro proyecto para que acepte el sistema de usuarios, para ello ejecutaremos estos comandos en nuestra terminal:

rails generate devise:install
rails generate devise user

Seguido de esto, nos aparecerá un mensaje en el cual Devise nos recuerda un par de cosas que debemos realizar, las traduciré para ustedes:

1. Asegúrate de haber definido tu url por defecto en tu variable de entorno. Aquí hay un ejemplo apropiado para desarrollo de el archivo config/environments/development.rb:

config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

En producción, :host debe estar asignado al host actual de tu aplicación.

2. Asegúrate de tener definida tu ruta inicial en el archivo config/routes.rb

root "home#index"

3. Asegúrate que tus layouts presentan los mensajes flash, por ejemplo en app/views/layouts/application.html.erb:

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

4. Si vas a utilizar Heroku con Rails versión 3.2 (sólo esta versión), quizá debas asignar la siguiente variable en config/application.rb:

config.assets.initialize_on_precompile = false

5. Puedes copiar las vistas de Devise (con el fin de editarlas) a tu app haciendo uso del comando:

rails generate devise:views

Luego de haber realizado todos estos pasos, tendrás creado un modelo de usuario con los siguientes campos (pueden agregarle más después):

Nos aseguramos de realizar las migraciones para que estos cambios tomen efecto en la base de datos:

rake db:migrate

Y además, devise tiene un controlador propio con varias acciones como por ejemplo, inicio de sesión, registro, cierre de sesión y recuperación de contraseñas, todo esto, gracias al mágico comando que encontrarán en su config/routes.rb:

devise_for :user

Instalando Omniauth

Esta gema no tiene mucha ciencia tampoco, pero antes de empezar a desarrollar esta sección, debemos hacer unas cuantas configuraciones. Primero que nada, agreguemos las gemas que utilizaremos:

gem "omniauth"
gem "omniauth-facebook"
gem "omniauth-twitter"

Y ejecutamos en nuestra terminal favorita:

bundle install

Listo, tenemos la gema instalada, ahora debemos considerar dos cosas, primero, es necesario llevar una clase que nos permita determinar, a partir de cierta llamada desde nuestra red social escogida para realizar login, el usuario a quien está asociado, por eso crearemos nuestro modelo llamado Entities (entidades) que harán referencia a un usuario (y más de alguna puede señalar a más de un usuario, supongamos que inicio sesión con Facebook y en otra ocasión con Twitter, la idea es asignar el mismo usuario). Nuestro modelo quedaría:

Entity:

  • user_id # usuario a quien referencia
  • provider # quien provee este login (red social)
  • uid # id único generado por el proveedor

Y para crearlo ejecutamos el siguiente comando:

rails g model identity user:references provider:string uid:string

Tal como lo realizamos en el paso anterior, debemos realizar nuestras migraciones en la base de datos:

rake db:migrate

Listo, lo tenemos el modelo creado y nuestra base de datos, ahora procederemos a modificarlo un poco (app/models/identity.rb):

class Identity < ActiveRecord::Base
  belongs_to :user
  validates_presence_of :uid, :provider
  validates_uniqueness_of :uid, :scope => :provider

  def self.find_for_oauth(auth)
    find_or_create_by(uid: auth.uid, provider: auth.provider)
  end
end

Como pueden ver, la referencia que creamos en el comando está marcada en la segunda línea, luego las validaciones son para que el uid que nos entrega el proveedor esté presente y además sea único entre los que pertenecen a un proveedor en particular.

Si recuerdan los códigos de cliente y secretos que solicitamos al comienzo de este tutorial, pues bien, ahora los necesitaremos, así que abriremos el archivo config/initializers/devise.rb y agregamos las siguientes líneas al final:

config.omniauth :facebook, "Cliente", “Secreto"
config.omniauth :twitter, "Cliente", “Secreto"

(Si quieren, pueden realizar este paso de una forma más segura en el tutorial que les enseña Gonzalo en el siguiente link http://blog.desafiolatam.com/recuperar-contrasenas-con-devise-y-gmail/)

Bueno, tenemos configurado Devise y Omniauth para que nos acepten usar Facebook y Twitter y además almacenamos las llamadas al proveedor, pero… ¿cómo hago que se inicie sesión? ¿qué controlador realiza los llamados? ¡A configurar!

Primero generemos el controlador que nos permitirá realizar el login luego de que nos llegue una respuesta desde alguna red social, para ello, crearemos un nuevo controlador en app/controllers llamado omniauth_callbacks_controller.rb con el siguiente contenido:

class OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def twitter
    @user = User.find_for_oauth(env["omniauth.auth"], current_user)

    if @user.persisted? # Chequea que nuestro usuario se haya guardado en la base de datos y no sea una instancia superficial
      sign_in_and_redirect @user, event: :authentication
      set_flash_message(:notice, :success, kind: "twitter".capitalize) if is_navigational_format?
    else
      session["devise.twitter_data"] = env["omniauth.auth"]
      redirect_to new_user_registration_url
    end
  end

  def facebook
    @user = User.find_for_oauth(env["omniauth.auth"], current_user)

    if @user.persisted? # Chequea que nuestro usuario se haya guardado en la base de datos y no sea una instancia superficial
      sign_in_and_redirect @user, event: :authentication
      set_flash_message(:notice, :success, kind: "facebook".capitalize) if is_navigational_format?
    else
      session["devise.facebook_data"] = env["omniauth.auth"]
      redirect_to new_user_registration_url
    end
  end

  def after_sign_in_path_for(resource) # Revisa después de cada login si el mail del usuario es válido
    if resource.email_verified?
      super resource # Acción por defecto de Devise (si no está configurada, va al root_path)
    else
      finish_signup_path(resource)
    end
  end

end

Ahora, debemos asegurarnos de que Devise ocupe nuestro controlador para estas acciones, así que editaremos nuestro config/routes.rb para que aparezca así:

devise_for :users, controllers: { omniauth_callbacks: 'omniauth_callbacks' }

Los métodos Facebook y Twitter son los encargados de retomar el login luego que una de estas redes sociales nos haya devuelto la información acerca de la sesión respectiva, y como pueden apreciar, hacen el llamado a una función de User que no hemos creado aún, así que procederemos a agregarla en nuestro modelo (app/models/user.rb):

class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable,
         :omniauthable, omniauth_providers: [:twitter, :facebook]

  def self.find_for_oauth(auth, signed_in_resource = nil)
    identity = Identity.find_for_oauth(auth)
    user = signed_in_resource ? signed_in_resource : identity.user

    if user.nil?
      email = auth.info.email
      user = User.find_by(email: email) if email

      # Create the user if it's a new registration
      if user.nil?
        password = Devise.friendly_token[0,20]
        if auth.provider == 'facebook'
          user = User.new(
            email: email ? email : "#{auth.uid}@change-me.com",
            password: password,
            password_confirmation: password
          )
        elsif auth.provider == 'twitter'
          user = User.new(
            email: "#{auth.uid}@change-me.com",
            password: password,
            password_confirmation: password
          )
        end
      end
      user.save!
    end

    if identity.user != user
      identity.user = user
      identity.save!
    end
    
    user
  end

  def email_verified?
    if self.email
      if self.email.split('@')[1] == 'change-me.com'
        return false
      else
        return true
      end
    else
      return false
    end
  end
end

Como podrán apreciar, auth es el hash que nos entrega el proveedor del login (pueden revisarlos completos en: https://github.com/mkdynamic/omniauth-facebook  y https://github.com/arunagw/omniauth-twitter, en la sección Auth Hash), por ello, sacamos el email del usuario usando auth.info.email, pero este dato no existe en Twitter (su API no nos provee de email), es por ello que le asignamos un valor genérico y luego obligaremos al usuario a que asigne su email manualmente (este mismo caso sucede si es que no tenemos el permiso de ver su email desde Facebook), así que primero, crearemos el controlador de usuarios para que se haga cargo de esta acción:

rails generate controller users

Ahora, crearemos la acción que aparecía en nuestro OmniauthCallbackController, finish_signup, para ello abriremos app/controllers/users_controller.rb:

class UsersController < ApplicationController
  def finish_signup
    if request.patch? && params[:user] # Revisa si el request es de tipo patch, es decir, llenaron el formulario y lo ingresaron
      @user = User.find(params[:id])

      if @user.update(user_params)
        sign_in(@user, :bypass => true)
        redirect_to root_path, notice: 'Hemos guardado tu email correctamente.'
      else
        @show_errors = true
      end
    end
  end

  private
    def user_params
      accessible = [ :name, :email ] # extend with your own params
      accessible << [ :password, :password_confirmation ] unless params[:user][:password].blank?
      params.require(:user).permit(accessible)
    end
end

Acto seguido, crearemos el formulario para que esta acción pueda llevarse a cabo, para ello, crearemos el archivo app/views/users/finish_signup.html.erb y colocaremos esto en su contenido:

<h1>Confirma tu Email</h1>

<%= form_for(current_user, as: 'user', url: finish_signup_path(current_user)) do |f| %>
  <% if @show_errors && current_user.errors.any? %>
    <h2>Errores encontrados:</h2>
    <ul>
      <% current_user.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
    </ul>
  <% end %>
 
  <div>
    <%= f.label :email %>
    <%= f.text_field :email, autofocus: true, value: '', placeholder: 'Example: email@me.com' %>
  </div>

  <div>
    <%= f.submit 'Confirmar' %>
  </div>

<% end %>

Ya nos queda muy poco, sólo agregar esta acción a nuestro config/routes.rb:

match '/users/:id/finish_signup' => 'users#finish_signup', via: [:get, :patch], as: :finish_signup

Y notarán que le permitimos que esta acción pueda ser llamada como get para recibir el formulario y como patch para enviar los datos.

¡Y listo!

Ahora tienes completamente configurado tu proyecto Ruby on Rails para que funcione con Facebook y Twitter, de hecho, puedes probar desde tu PC en esta dirección http://localhost:3000/users/sign_in y aparecería algo así:

Si pinchan en Twitter (Facebook no funciona desde localhost, lamentablemente), los llevará a su cuenta de Twitter para iniciar sesión y autorizar la app, acto seguido, les pedirá su email (tal como les decía, Twitter no les da su email) y visualizarán nuestro formulario:

Y luego colocamos un mail válido, pinchamos “Confirmar” y habremos vuelto al root de nuestro sitio con todo funcionando.

Fuentes:

Artículos relacionados

Creando una página 404 con Rails

León Gleiser
9 años ago

Diseño, experiencias y servicios: ¿juntos o revueltos?

Cristian Carreño Escobar
5 años ago

Aplicaciones Android para distintos tipos de pantalla

Erick Navarro
8 años ago
Salir de la versión móvil