Dans mon introduction à OAuth, je vous avez laissé sur votre faim côté implémentation car ce n’était pas l’objet du billet.
J’avais juste brièvement évoqué omniauth comme l’outil indispensable.
Voyons donc aujourd’hui comment réellement intégrer cette solution avec devise.
Qu’elle que soit la plateforme en question (google, twitter, viadeo, facebook, linkedin…) vous aurez besoin de créer une application sur la plateforme cible. Par exemple sur twitter: https://dev.twitter.com/apps/new
Ce qui nous intéresse c’est de récupérer les clés qui nous permettront d’interagir avec l’application, à savoir le consumer key et le consumer secret.
En premier lieu, commençons par le plus simple, à savoir installer omniauth. Il suffit de l’ajouter dans le Gemfile.
gem 'omniauth'
Pour utiliser openid (pour s’authentifier via google par exemple), nous aurons besoin des gems suivantes:
gem 'oa-core'
gem 'oa-openid', :require => 'omniauth/openid'
Omniauth s’intègre très bien avec devise, il suffit de configurer votre initializer devise.rb. Il est de bon ton d’avoir des applications pour votre environnement de dev et d’autres pour votre production.
if Rails.env.development?
config.omniauth :twitter, 'CONSUMER_KEY', 'CONSUMER_SECRET'
config.omniauth :facebook, 'CONSUMER_KEY', 'CONSUMER_SECRET', :scope => 'email'
config.omniauth :linked_in, 'CONSUMER_KEY', 'CONSUMER_SECRET'
config.omniauth :viadeo, 'CONSUMER_KEY', 'CONSUMER_SECRET'
config.omniauth :google_apps, OpenID::Store::Filesystem.new('/tmp'), :domain => 'gmail.com'
else
config.omniauth :twitter, 'CONSUMER_KEY', 'CONSUMER_SECRET'
config.omniauth :facebook, 'CONSUMER_KEY', 'CONSUMER_SECRET', :scope => 'email'
config.omniauth :linked_in, 'CONSUMER_KEY', 'CONSUMER_SECRET'
config.omniauth :viadeo, 'CONSUMER_KEY', 'CONSUMER_SECRET'
config.omniauth :google_apps, OpenID::Store::Filesystem.new('/tmp'), :domain => 'gmail.com'
end
Dans le modèle qui gère vos utilisateurs il faut rajouter omniauthable:
# user.rb
devise :omniauthable
Nous allons indiquer à devise quel contrôleur utiliser lors de la réception des callbacks des différentes applications…
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }
…et créer le contrôleur correspondant:
rails g controller users/omniauth_callbacks
Notre contrôleur doit hériter de Devise::OmniauthCallbacksController et implémenter une action par service supporté.
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def linked_in
# some stuff
end
def twitter
end
end
Dès lors que vous avez configuré un service, le formulaire de login de devise vous offre immédiatement un lien pour se connecter avec ce service.
Quand vous suivrez ce lien depuis votre application, la plateforme cible (ex: twitter) vous demandera d’autoriser explicitement l’utilisation de vos informations.
Si vous acceptez vous serez redirigé vers l’application que vous développez. A la redirection un hash (env[“omniauth.auth”]) est transmis. C’est là que vous trouverez toutes les informations de l’utilisateur selon la plateforme (email, nom, prénom…)
Beaucoup d’applications prennent la liberté de créer des comptes utilisateurs à la volée au retour de ces informations, en générant un mot de passe, voire un email, aléatoire.
Je pense personnellement que c’est une mauvaise idée. En faisant cela l’utilisateur ne comprendra pas qu’il vient de créer un compte sur votre application, donc il risque fort de créer 1 compte par plateforme utilisée.
De votre côté vous allez vous retrouver avec des données inconsistantes.
Nous allons créer un modèle authorization qui permettra de stocker les informations propres à chaque service permettant de s’authentifier.
Cette autorisation est rattachée à un utilisateur, les champs indispensables sont donc:
rails g model Authorization user:references uid:string provider:string token:string secret:string
# user.rb
has_many :authorizations, :dependent => :destroy
# authorization.rb
belongs_to :user
Si vous êtes connecté, vous avez un current_user donc vous pouvez créer une authorization depuis votre contrôleur.
Depuis l’espace où vous êtes connecté dans votre application vous pouvez lister les autorisations présentes et permettre à l’utilisateur de les révoquer.
Si vous n’êtes pas connecté, la première chose à faire est d’essayer de voir si une autorisation correspondante à l’uid récupéré dans le hash existe depuis le contrôleur de callback.
Si c’est le cas on récupère l’user rattaché à cette autorisation et il devient à la fois authentifié et notre current_user.
S’il n’y a pas d’autorisation correspondante c’est certainement que l’utilisateur veut se créer un compte.
Dans ce cas on va simplement récupérer les informations, les utiliser pour construire notre utilisateur et afficher la vue d’inscription.
Restera alors uniquement à l’utilisateur de rentrer son mot de passe et son email si celui ci n’est pas fourni, ce qui est un confort non négligeable.
L’intégration de google m’a posé quelques problèmes, lorsque j’étais authentifié et que je tentais d’autoriser google, au retour mon current_user était nil, la session avait été remise à 0.
En fouillant un peu je me suis rendu compte qu’un ticket avait été ouvert. Cela vient apparemment du formulaire d’envoi des paramètres à google qui ne contient pas le token de protection.
Ce token n’étant pas envoyé, rails fait un reset de la session.
Il y a deux solutions, surcharger le formulaire existant de omniauth pour google en utilisant le helper form_tag qui inclue tout seul le token, ou dans notre contrôleur de callback contourner la vérification:
protect_from_forgery :except => :google_apps
L’équipe Synbioz.
Libres d’être ensemble.
Nos conseils et ressources pour vos développements produit.