Criando uma autenticação com Devise + Facebook

Tutoriais - 03/Fev/2021 - por Rafael Aquino

1. Introdução

O Oauth 2 é um protocolo que fornece um fluxo específico de autorização para aplicações web, desktop e mobile. Dessa forma, o usuário consegue ter acesso a aplicações de terceiros sem expor suas credenciais, apenas delegando a sua autenticação ao serviço que hospeda a conta dele, como por exemplo o Facebook, Github ou Twitter.

A imagem abaixo ilustra o fluxo e os papéis definidos pelo Oauth2:

1 - O usuário da aplicação (Resource Owner) solicitará acesso a nossa aplicação (Client Application) através do nosso provider ao invés de fazer o login utilizando o formulário.

2- Como resposta, ele será redirecionado para o servidor de autorização (Authorization Server) para permitir acesso aos seus dados naquele determinado provider

3 - Após permitir o acesso, nossa aplicação receberá um token de acesso para que possa consumir os dados do servidor de recurso (Resource Server)

2. Exemplificando

Para ficar mais claro, vamos implementar um exemplo utilizando o Facebook como fornecedor para logarmos na nossa aplicação Rails.

Vamos utilizar as gems Devise e omniauth-facebook para implementar a autenticação. Siga as instruções de instalação dessas gems seguindo a documentação linkada acima.

O primeiro passo é adicionar dois campos ao nosso model de usuário (User): provider e uid. Fazemos isso criando uma migration:

class AddOmniauthToUsers < ActiveRecord::Migration[6.0]
 def change
   add_column :users, :provider, :string
   add_column :users, :uid, :string
 end
end

A coluna provider irá armazenar qual o nome do fornecedor de serviço que está sendo utilizado para fazer a autenticação. No nosso exemplo será 'Facebook'

A coluna uid irá armazenar um identificador único daquele usuário em dado provider. No nosso exemplo, o Facebook ID do usuário

Depois, no config/initializers/devise, declaramos o provider: config.omniauth :facebook, ENV["APP_ID"], ENV["APP_SECRET"].

2.1 Gerando as chaves

Para gerar o APP_ID e o APP_SECRET acesse developers.facebook.com e faça seu login, caso já não esteja logado.

Vá em Meus aplicativos e depois clique em Criar Aplicativo.

Escolha Criar experiências conectadas e clique em Continuar.

Preencha o campo Nome de exibição do aplicativo com o nome do aplicativo e clique em Criar aplicativo. Após isso vá em Login Facebook e clique em Configurar.

Na tela seguinte, escolhemos a opção Web.

O campo da URL do site, preencha com http://localhost:3000 e clique em Save.

Vale lembrar que é importante verificar no menu superior, se está em Modo de Desenvolvimento.

No menu lateral vá em Configurações -> Básico e lá você terá acesso ao seu APP_ID e APP_SECRET.

Copie esses dados e os adicione no seu arquivo .env na raiz do projeto. Eles devem ficar como no trecho de código abaixo:

APP_ID=1560871004116268
APP_SECRET=3912830dad8jU987987DS215

Lembrando que não se deve enviar o arquivo .env para repositórios como Github, pois suas chaves ficarão públicas e outro usuário poderia usá-las se passando por você.

2.2 Configurando

No seu model de usuário, adicione :omniauthable, omniauth_providers: %i[facebook] na configuração do Devise. ``` class User < ApplicationRecord devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable, :omniauthable, omniauth_providers: %i[facebook] end


Após esse passo, se já tiver adicionado o `devise_for :user` no seu arquivo de rotas, é gerada uma rota `user_facebook_omniauth_callback_path` que será utilizada como *callback* após o login pelo Facebook.

Iremos sobrescrever o controller padrão do devise **omniauth_callbacks**, para que possamos implementar a lógica necessária para tratarmos os dados de callback após logarmos.
Faremos isso alterando o arquivo `config/routes` como no código abaixo:


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

Depois de alterar a rota precisaremos criar o controller em app/controllers/users/omniauth_callbacks_controller.rb.

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
 skip_before_action :verify_authenticity_token, only: :facebook

 def facebook
   @user = User.from_omniauth(request.env["omniauth.auth"])

   if @user.persisted?
     sign_in_and_redirect @user, event: :authentication
     set_flash_message(:notice, :success, kind: "Facebook") if is_navigational_format?
   else
     session["devise.facebook_data"] = request.env["omniauth.auth"].except(:extra)
     redirect_to new_user_registration_url
   end
 end

 def failure
   redirect_to root_path
 end
end

Nesse controller temos alguns pontos a destacar:

1) Toda informação retornada pelo Facebook através dessa autenticação está disponível na Hash request.env["omniauth.auth"].

2) Quando um usuário válido é encontrado, ele pode logar com dois métodos distintos do Devise: o sign_in ou sign_in_and_redirect.

3) Uma Flash Message também poderá ser usada nas mensagens padrão do Devise, fica a seu critério.

4) No caso do usuário não estiver persistindo, nós armazenamos os dados da autenticação na sessão e então o direcionamos para a página de registro.

Após criar o controller precisamos ir no nosso model de usuário e criar o método from_omniauth no arquivo app/models/user.rb. Esse método tentará encontrar um usuário existente a partir do provider que é passado e do uuid. Se o usuário não for encontrado um novo será criado com uma senha aleatória.

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

 def self.from_omniauth(auth)
   where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
     user.email = auth.info.email
     user.password = Devise.friendly_token[0, 20]
   end
 end
end

Por fim para criar o botão de logout adicionamos a rota:

delete 'sign_out', :to => 'devise/sessions#destroy', :as => :destroy_user_session

Ficando, por fim, assim:

  devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' } do
    delete 'sign_out', :to => 'devise/sessions#destroy', :as => :destroy_user_session
  end

Depois disso, deve ser possível realizar o login na sua aplicação usando o Facebook tornando mais ágil a utilização do seu site/serviço pelo usuário.

Além do Facebook, também existem diversas alternativas para fazer o processo de autenticação do seu site, dentre elas o Twitter, Google, LinkedIN e a lista não acaba por aí.

Referências

Foto de perfil do autor
Rafael Aquino

Dev