Pour un projet Ionic utilisant une identification Facebook avec ng-tocken-auth en front et devisetockenauth en back, je suis venu à avoir besoin d'enregistrer lors de la création de l'utilisateur sa date de naissance.

Hors, malgré notre configuration et validation de l'application côté Facebook, cette donnée n'était pas disponible. Je me suis même aperçu que le bloc extra en entier n'était pas présent dans la session.

Configuration de la gem

Omniauth est configuré pour utiliser Facebook et pour lui demander les extras suivant first_name, last_name, gender, birthday.

# config/initializers/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
  provider :facebook, ENV['FACEBOOK_KEY'], ENV['FACEBOOK_SECRET'], :scope => 'email,public_profile,user_birthday', 
    :image_size => 'large', :info_fields => 'first_name, last_name, gender, birthday'
end

L'information fournie par Facebook

Voici ce que byebug nous affiche pour le contenu de request.env['omniauth.auth'].

(byebug) request.env['omniauth.auth']
#<OmniAuth::AuthHash credentials=#<OmniAuth::AuthHash expires=true expires_at=1469477727 token="EAAHw4jqZBKe8BAKI4rri2VvkWU7g0XUssFA1KzsiZBviXZBbUzSd1ZByzc4Q7r67Cb1z8CJeNy1Twkc4gehMUtGY4HDS5GT1h30rZCKMfHoRIAQJf743eZAF7IMP8mQESHwMuBs5aZAGDq6NEnlLZCTnH8CP5rNiQYX7CpwmvAtYVAZDZD"> extra=#<OmniAuth::AuthHash raw_info=#<OmniAuth::AuthHash birthday="03/31/1968" first_name="Alain" gender="male" id="1727512764150849" last_name="Andre">> info=#<OmniAuth::AuthHash::InfoHash first_name="Alain" image="http://graph.facebook.com/1727512764150849/picture?type=large" last_name="Andre"> provider="facebook" uid="1727512764150849">

omniauth.auth contient bien le bloc extra et ce dernier contient bien la donnée birthday ; tout ce que j'ai demandé est présent.

(byebug) request.env['omniauth.auth']['extra']
#<OmniAuth::AuthHash raw_info=#<OmniAuth::AuthHash birthday="03/31/1968" first_name="Alain" gender="male" id="1727512764150849" last_name="Andre">>

La perte des données

Lors du passage dans la fonction getresourcefromauthhash() du contrôleur OmniauthCallbacksController, nous n'avons plus accès à request.env['omniauth.auth'].

Il nous faut utiliser session['dta.omniauth.auth'] qui a perdu les informations du bloc extra.

(byebug) session['dta.omniauth.auth'] 
{"provider"=>"facebook", "uid"=>"1727512764150849", "info"=>{"first_name"=>"Alain", "last_name"=>"Andre", "image"=>"http://graph.facebook.com/1727512764150849/picture?type=large"}, "credentials"=>{"token"=>"EAAHw4jqZBKe8BACrZAxE8XNGYCBGe8VvrZA8sSFKixFaW79kt8nWUfGz7QxtN4K78YdZC9IiZBFHxxajusHBLZB5ZCpJcWqWFftDdooo0pQEZBPbbRWGg2ZBYCICRrfKco2KfmWQ7hIMUFShA50x4B7vVo0EpryNZBTciVNIvKLZAysiAZDZD", "expires_at"=>1469477727, "expires"=>true}}

En fait, la fonction redirect_callback() crée la session en retirant le bloc extra. Il est impossible d'y accéder par la suite.

session['dta.omniauth.auth'] = request.env['omniauth.auth'].except('extra')

La solution

Pour résoudre ce problème, il suffit de créer son propre OmniauthCallbacksController et de faire pointer la route dessus.

Pour cella nous créons ce contrôleur dans un répertoire nommé customs qui étend DeviseTokenAuth::OmniauthCallbacksController et ré-écrivons les fonctions qui nous font défaut.

La fonction redirect_callbacks() pour éviter un CookieOverflow retire l'extra de la session, on le conditionne donc pour Twitter seulement.

Reste à intégrer dans la fonction assignproviderattrs() les champs que l'on souhaite utiliser lors de la création de notre utilisateur.

# app/controllers/customs/omniauth_callbacks_controller.rb
module Customs
  class OmniauthCallbacksController < DeviseTokenAuth::OmniauthCallbacksController

    # intermediary route for successful omniauth authentication. omniauth does
    # not support multiple models, so we must resort to this terrible hack.
    def redirect_callbacks
      # derive target redirect route from 'resource_class' param, which was set
      # before authentication.
      devise_mapping = [request.env['omniauth.params']['namespace_name'],
                        request.env['omniauth.params']['resource_class'].underscore.gsub('/', '_')].compact.join('_')
      redirect_route = "#{request.protocol}#{request.host_with_port}/#{Devise.mappings[devise_mapping.to_sym].fullpath}/#{params[:provider]}/callback"

      # preserve omniauth info for success route. ignore 'extra' in twitter
      # auth response to avoid CookieOverflow.
      session['dta.omniauth.auth'] = (params[:provider] == 'twitter') ? request.env['omniauth.auth'].except('extra') : request.env['omniauth.auth']
      session['dta.omniauth.params'] = request.env['omniauth.params']

      redirect_to redirect_route
    end

    protected

      # break out provider attribute assignment for easy method extension
      def assign_provider_attrs(user, auth_hash)
        user.assign_attributes({
          nickname: auth_hash['info']['nickname'],
          name:     auth_hash['info']['name'],
          image:    auth_hash['info']['image'],
          email:    auth_hash['info']['email'],
          date_naissance: Date.strptime(auth_hash['extra']['raw_info']['birthday'], '%m/%d/%Y'),
          genre: auth_hash['extra']['raw_info']['gender']
        })
      end

  end
end

Finalement, il faut modifier la route pour qu'elle utilise notre contrôleur plutôt que celui par défaut.

# config/routes.rb
Rails.application.routes.draw do
  mount_devise_token_auth_for 'User', at: 'auth', controllers: {
    omniauth_callbacks: 'customs/omniauth_callbacks'
  }

Publié dans les catégories suivantes

javascriptruby
comments powered by Disqus

Téléphone

+33 637 700 504

Adresse

Bordeaux, 33300
France