Omniauth com vários provedores em um aplicativo Rails / Mongoid

NOTAS: Presumo que você já tenha o básico em ordem: você instalou as gemas, configurou omniauth com as chaves e segredos para os diferentes provedores de acordo com o seu caso, Você tem rotas definidas para responder aos diferentes provedores retornos de chamada e você tem um modelo de usuário no qual trabalhar com um método específico de modelo para criar usuários a partir de um hash omniauth.

Então, outro dia eu estava trabalhando em um projeto onde precisava do omniauth com vários provedores em um aplicativo Rails que usava Mongoid como ORM. Não foi muito difícil, mas certamente teve suas paritcularidades, vejamos:

Primeiro de tudo, temos que definir algumas configurações para nosso arquivo “Mongoid.yml” , esta etapa é mais fácil de explicar se eu postar uma imagem:

Cenário

Como você pode ver, precisamos de um hash de opções “filho direto” para nosso ambiente, definindo a opção “aumentar o erro não encontrado” como falso , pois precisamos obter um nil se nenhum registro for encontrado em vez de uma exceção.

Então, precisamos ter um modelo de usuários com um modelo de identidades referenciadas . Na minha configuração eu estava usando o Devise, então o modelo de usuário foi criado automaticamente e eu só tive que criar o modelo de identidades.

Os modelos (aparados):

Modelo de Usuário

class User
include
Mongoid::Document
has_many
:identities, :dependent => :destroy
...
end

Modelo de Identidade

class Identity
include
Mongoid::Document
belongs_to
:user
field
:uid, type: String
field
:provider, type: String

index
({ uid: 1}, {drop_dups: false, background: true})

end

Como você pode ver, eu indexei as identidades que um usuário pode ter porque a forma como isso funciona é pesquisar em nosso banco de dados por uma identidade existente para aplicar diferentes condicionais como veremos mais tarde … (Indexá-los torna isso rápido como o inferno haha); É importante evitar o descarte de cópias porque a lógica de que precisamos para implementar isso criará primeiro uma identidade sem “user_id” se a identidade que o usuário está usando para entrar nunca foi persistida em nosso banco de dados; Mais tarde, removeremos a duplicação, continue lendo.

Depois de terminarmos com isso, temos que modificar nosso controlador que responde aos diferentes retornos de chamada omniauth, os meus da seguinte maneira:

class OmniauthCallbacksController < Devise::OmniauthCallbacksController
skip_before_filter
:verify_authenticity_token
def all
auth
= request.env["omniauth.auth"]
# Find an identity here
@identity = Identity.all.find_by(:uid => auth.uid, :provider => auth.provider)

if @identity.nil?
# If no identity was found, create a brand new one here
@identity = Identity.create(:uid => auth.uid, :provider => auth.provider)
end
if signed_in?
if @identity.user == current_user
# User is signed in so they are trying to link an identity with their
# account. But we found the identity and the user associated with it
# is the current user. So the identity is already associated with
# this user. So let's display an error message.
redirect_to user_url
, notice: "Already linked that account!"
else
# The identity is not associated with the current_user so lets
# associate the identity
@identity.user = current_user
@identity.save()
redirect_to user_url
, notice: "Successfully linked that account!"
end
else
if @identity.user.present?
# The identity we found had a user associated with it so let's
# just log them in here
user
= @identity.user
flash
.notice = "Signed in!"
sign_in_and_redirect user

else
# Logic for the case when we actually need to create a new user
user
= User.from_omniauth(auth)
if user.persisted?
flash
.notice = "Signed in!"
sign_in_and_redirect user

else
session
["devise.user_attributes"] = user.attributes
redirect_to new_user_registration_url

end
end
end
end
alias_method
:facebook, :all
alias_method
:twitter, :all
alias_method
:google_oauth2, :all
end

Basicamente, você pode usar o mesmo controlador que o meu, (pulando a herança do devise se você não estiver usando o Devise) e substituindo o Logic para o caso em que realmente precisamos criar uma nova parte de usuário para você, dependendo do seu caso. O que esta parte deve fazer é criar um usuário a partir do hash omniauth. Eu adicionei alguma lógica extra para a configuração do meu dispositivo, mas não é realmente importante. Outra coisa a se estar ciente é que você deve configurar sua própria ação de acordo com o ambiente de seu aplicativo, caso a identidade que encontramos tenha um usuário (user.present?). No meu controlador estou usando o sinal “específico do dispositivo e redirecionar o método do usuário “. Observe os apelidos para todosmétodo fornecendo uma “ação diferente” (mas na verdade retornando o mesmo método) para os diferentes provedores que queremos suportar e a ordem dos argumentos no find_by e create! ações que obedecem a estrutura do nosso modelo de identidade e a forma como o índice foi criado, para uma utilização eficiente do índice. Finalmente, vamos dar uma olhada no meu método User.from_omniauth :

def self.from_omniauth(auth)
where(auth.slice(:uid, :provider)).first_or_create do |user|
user
.uid = auth.uid
user
.provider = auth.provider
user
.name = auth.info.name
user
.email = auth.info.email
user
.image = auth.info.image
end
end

Este método só é acionado quando estamos realmente criando um novo usuário a partir de um hash omniauth.

Mais uma coisa … Você se lembra da duplicação de identidade que discutimos anteriormente? Isso pode ser resolvido com um simples retorno de chamada em nosso modelo de usuário, que é acionado após a criação do usuário:

after_create :first_identity

def first_identity
self.identities.first_or_create!(:uid => self.uid, :provider => self.provider, :user_id => self.id)
Identity.where(:user_id => nil).destroy_all
end

E … basicamente é isso. Acho que não esqueci de nada. Aí estão você, instruções para implementar o omniauth com vários provedores em seu aplicativo Mongoid / Rails.

PS Se você estiver recebendo erros de “Token inválido” ao fazer algumas solicitações POST por meio de uma forma de algo em seu aplicativo como um usuário criado recentemente, adicione esta linha à seção head de seu layout:

<%= csrf_meta_tag %> 

Logo abaixo das <% = csrf meta tags%> .

C’ya! 😉