Intégrer les réseaux sociaux dans son application ruby on rails avec OAuth

Publié le 18 octobre 2011 par Martin Catty | outils

Cet article est publié sous licence CC BY-NC-SA

Intégrer les réseaux sociaux dans son application ruby on rails avec omniauth

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.

Créer des applications sur les plateformes ciblées

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.

Configurer notre application rails

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'

Configurer devise

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

Configurer notre modèle

Dans le modèle qui gère vos utilisateurs il faut rajouter omniauthable:

# user.rb
devise :omniauthable

Configurer nos routes et notre contrôleur

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…)

Gestion des autorisations

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.

Créer un modèle authorization

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

Utilisateur connecté

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.

Utilisateur déconnecté

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.

Problèmes rencontrés

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

Documentation complémentaire

  • La doc de devise
  • Ce gist est un bon point de départ, mis à part qu’il crée des comptes à la volée, et que la quasi totalité du code privé du contrôleur devrait être déporté dans le modèle authorization.

L’équipe Synbioz.

Libres d’être ensemble.