I. Avant-propos

II. Préparation

Avant de commencer ce tutoriel, vous aurez besoin de ce qui suit :

Pour consulter la source, exécutez les commandes suivantes (ou utilisez un outil approprié pour Git GUI) :

 
Sélectionnez
git clone git@github.com:alexyoung/dailyjs-backbone-tutorial.git
cd dailyjs-backbone-tutorial
git reset --hard 9d09a66b1f

III. L'API cliente Google OAuth 2.0

Ouvrez app/js/gapi.js et jetez un œil aux lignes 11 à 25. Il y a une méthode, fournie par Google, appelée gapi.auth.authorize. Celle-ci utilise l'ID client et certaines extensions pour tenter de s'authentifier. J'ai déjà mis les extensions dans app/js/config.js :

 
Sélectionnez
config.scopes = 'https://www.googleapis.com/auth/tasks https://www.googleapis.com/auth/userinfo.profile';

Ceci indique au système d'authentification que notre application souhaite accéder au profil de l'utilisateur et aux tâches de Gmail. Tout est presque prêt à fonctionner, mais deux choses manquent : une implémentation pour handleAuthResult et une interface.

IV. Templates

RequireJS peut charger des templates en utilisant le plugin texttext. Téléchargez text.jstext.js sur GitHub et enregistrez-le sur app/js/lib/text.js.

Il s'agit de ma méthode préférée pour gérer des templates. Bien que cette application pourrait facilement être placée dans un fichier index.html monolithique, diviser des projets en templates plus petits est plus facile à gérer à long terme, donc c'est une bonne idée de s'habituer à cette manière de faire.

Maintenant, ouvrez app/js/main.js et ajoutez le plugin text dans la configuration de RequireJS :

 
Sélectionnez
paths: {
  text: 'lib/text'
},

Enfin, ajoutez ceci à app/js/config.js :

 
Sélectionnez
_.templateSettings = {
  interpolate: /\{\{(.+?)\}\}/g
};

Cela indique au système de templates de Underscore d'utiliser deux accolades pour insérer des valeurs, autrement connu comme l'interpolation.

L'application a besoin de certains répertoires pour stocker des choses liées au template :

  • app/js/views/ : celui pour les vues Backbone.js ;
  • app/js/templates/ : les templates HTML à charger par les vues ;
  • app/css.

Le fichier app/index.html doit charger le CSS. Ajoutez une balise de lien pour cela :

 
Sélectionnez
<link rel="stylesheet" href="css/app.css">

Et créer un fichier CSS approprié dans app/css/app.css :

 
Sélectionnez
#sign-in-container, #signed-in-container { display: none }

L'application démarre en masquant le bouton de connexion et le contenu principal. L'API Oauth va être interrogée sur les informations d'identification utilisateur existantes. Si l'utilisateur a déjà ouvert une session récemment, ses coordonnées seront stockées dans un cookie donc les vues doivent être configurées de façon appropriée.

Les templates ne sont pas particulièrement visibles à ce stade. Mettez ceci dans app/js/templates/app.html :

 
Sélectionnez
<div class="row-fluid">
  <div class="span2 main-left-col" id="lists-panel">
    <h1>bTask</h1>
    <div class="left-nav"></div>
  </div>
  <div class="main-right-col">
    <small class="pull-right" id="profile-container"></small>
    <div>
      <div id="sign-in-container"></div>
      <div id="signed-in-container">
        <p>Vous êtes connecté !</p>
      </div>
    </div>
  </div>
</div>

Ce modèle présente certaines choses que nous ne devons pas encore utiliser. Ignorez-les pour l'instant et concentrez-vous sur sign-in-container et signed-in-container.

Ensuite, collez ce qui suit dans app/js/templates/auth.html :

 
Sélectionnez
<a href="#" id="authorize-button" class="btn btn-primary">S'authentifier avec Google</a>

Le template auth.html est inséré dans sign-in-container. C'est très simple pour le moment, je l'inclus seulement dans le but de créer des vues de Backbone.js supplémentaires afin de voir comment cela fonctionne.

V. Les vues Backbone

Ces modèles doivent correspondre aux vues Backbone.js pour les gérer. Cette partie montre comment charger des templates avec RequireJS et les afficher. Créez un fichier nommé app/js/views/app.js :

 
Sélectionnez
define([
  'text!templates/app.html'
],
 
function(template) {
  var AppView = Backbone.View.extend({
    id: 'main',
    tagName: 'div',
    className: 'container-fluid',
    el: 'body',
    template: _.template(template),
 
    events: {
    },
 
    initialize: function() {
    },
 
    render: function() {
      this.$el.html(this.template());
      return this;
    }
  });
 
  return AppView;
});

La classe AppView n'a pas encore tous les événements, mais elle sait lier des éléments, le body et charger un template : define(['text!templates/app.html']. La directive text! est assurée par le plugin text de RequireJS que nous avons ajouté plus tôt. Le template lui-même est simplement une chaîne qui contient le code HTML correspondant. Il est affiché en le liant à Backbone.View, puis en appelant la méthode html() de jQuery qui remplace le code HTML dans un élément : this.$el.html(this.template());.

Le AuthView est un peu différent. Créez un fichier nommé app/js/views/auth.js :

 
Sélectionnez
define(['text!templates/auth.html'], function(template) {
  var AuthView = Backbone.View.extend({
    el: '#sign-in-container',
    template: _.template(template),
 
    events: {
      'click #authorize-button': 'auth'
    },
 
    initialize: function(app) {
      this.app = app;
    },
 
    render: function() {
      this.$el.html(this.template());
      return this;
    },
 
    auth: function() {
      this.app.apiManager.checkAuth();
      return false;
    }
  });
 
  return AuthView;
});

L'objet app est passé à initialize lors de l'instanciation de AuthView (avec AuthView(this) par la suite). La raison pour laquelle j'ai fait cela est de permettre à la vue d'appeler le code d'authentification requis par ApiManager. Cela pourrait aussi être géré avec les événements ou bien d'autres manières mais je voulais juste montrer que nous pouvons initialiser des vues avec des valeurs comme n'importe quelle autre classe.

VI. Cœur de l'application

Les vues doivent être instanciées et affichées. Ouvrez app/js/app.js et modifiez-le pour charger les vues à l'aide de RequireJS :

 
Sélectionnez
define([
  'gapi'
, 'views/app'
, 'views/auth'
],
 
function(ApiManager, AppView, AuthView) {
  var App = function() {
    this.views.app = new AppView();
    this.views.app.render();
 
    this.views.auth = new AuthView(this);
    this.views.auth.render();
 
    this.connectGapi();
  }

Le reste du fichier peut rester le même. Notez que l'ordre d'affichage des vues est important. AuthView ne fonctionnera pas à moins qu'il possède certaines des balises disponibles de AppView. Une meilleure façon de modéliser pourrait consister à déplacer AuthView à l'intérieur de AppView donc la dépendance se transmet. Vous pouvez essayer vous-même si vous voulez vous exercer.

VII. Implémentation de l'authentification

Le fichier app/js/gapi.js n'a toujours pas la fonction handleAuthResult, donc rien ne fonctionne pour l'instant. Voici le code pour gérer l'authentification :

 
Sélectionnez
function handleAuthResult(authResult) {
  var authTimeout;
 
  if (authResult && !authResult.error) {
    // Planifier une vérification lorsque le jeton d'authentification expire
    if (authResult.expires_in) {
      authTimeout = (authResult.expires_in - 5 * 60) * 1000;
      setTimeout(checkAuth, authTimeout);
    }
 
    app.views.auth.$el.hide();
    $('#signed-in-container').show();
  } else {
    if (authResult && authResult.error) {
      // Montrer les erreurs
      console.error('Unable to sign in:', authResult.error);
    }
 
    app.views.auth.$el.show();
  }
}
 
this.checkAuth = function() {
  gapi.auth.authorize({ client_id: config.clientId, scope: config.scopes, immediate: false }, handleAuthResult);
};

L'astuce pour obtenir une fluidité de l'authentification est de déterminer le moment où l'utilisateur est déjà connecté. Alors l'authentification peut être gérée de manière transparente, sinon l'utilisateur est invité à s'authentifier.

La fonction handleAuthResult est appelée par gapi.auth.authorize de la fonction checkAuth, laquelle n'est pas affichée ici (elle se trouve avant handleAuthResult dans le fichier source si vous voulez le vérifier). La méthode this.checkAuth est différente. Il s'agit d'une méthode publique qui appelle gapi.auth.authorize avec un positionnement immédiat à false, tandis que l'autre invocation est appelée avec true.

L'option immediate est essentielle car elle détermine si une fenêtre contextuelle s'affiche ou non. Je l'ai utilisée pour vérifier si l'utilisateur est déjà connecté, sinon elle est appelée à nouveau avec immediate: false et affiche un menu contextuel approprié afin que l'utilisateur puisse consulter les autorisations que l'application veut utiliser :

Image non disponible

J'ai conçu cette façon de faire basée sur la documentation des API Google de bibliothèques clientes pour JavaScript :

« La méthode standard authorize() affiche toujours une popup qui peut être un peu brusque si vous essayez seulement d'actualiser un jeton OAuth 2.0. La mise en place de Google OAuth 2.0 prend également en charge le mode "immediate" qui actualise un jeton sans popup. Pour utiliser ce mode, il suffit d'ajouter "immediate: true" à la configuration de connexion comme dans l'exemple ci-dessus. »

J'ai aussi changé la classe ApiManager pour stocker une référence à l'application :

 
Sélectionnez
// Au début de gapi.js
var app;
 
function ApiManager(_app) {
  app = _app;
  this.loadGapi();
}

VIII. Résumé

Dans ce tutoriel, vous avez vu comment utiliser les API Google pour vous connecter à une application que vous avez précédemment enregistrée avec l'API Google Console (documenté en partie 2). On pourrait croire que c'est beaucoup de travail pour obtenir RequireJS, Backbone.js et Google OAuth travaillant ensemble, mais pensez à ce que nous avons atteint : un script 100 % client capable de s'authentifier avec des comptes Google existants.

Si j'ai oublié quelque chose ici, vous pouvez vérifier la source complète (c1d5a2e7csource).

IX. Remerciements

Cet article a été publié avec l'aimable autorisation de Alex Young. L'article original peut être lu sur le site DailyJSDailyJS : Backbone.js Tutorial: Authenticating with OAuth2Backbone.js Tutorial: Authenticating with OAuth2.
Je remercie également ClaudeLELOUP pour sa relecture attentive et assidue.