Angular.js et Google OAuth API

Angular.js Google OAuth

Vous avez sans doute déjà entendu parler du framework Angular.js. Pour ceux dont ce ne serait pas le cas, sortez de vos cavernes !

Plus sérieusement, Angular.js est un framework MVW front-end. En quelques mots, il s’agit d’un framework Javascript, poussé par Google, utilisant des vues et des contrôleurs. À la différence de la plupart des framework connus, ce dernier est un framework front-end: il s’exécute donc sur le navigateur du client. Et c’est là toute ça force ! One-page, Two-way Data Binding, directives et filtres, Angular.js facilite grandement le développement d’applications légère et rapide.

Dans cet article, je n’entrerais pas dans les détails d’Angular.js mais je vous invite grandement à consulter la documentation officielle.

GitHub-Mark-Light-32px Exemple complet sur GitHub: Angular factory Google OAuth.

Les difficultés de l’authentification avec Angular.js

Comme je l’ai dit précédemment, Angular.js est un framework front-end. Il n’intègre donc pas les fonctionnalités qu’on pourrait retrouver sur un langage back-end (comme PHP) et ses avantages tels que la connexion à une base de données ou l’utilisation de sessions.

Même s’il existe diverses solutions pour créer des pseudo-sessions, notamment avec l’usage de cookies, je n’ai pas trouvé de solutions à mon goût. De ce fait, je me suis tourné vers les API d’authentifications existantes. J’ai choisi celle de Google.

Créer une application Google

Dans un premier temps, vous devez créer une application Google. Je pars du principe que vous avez déjà un compte Google. Rendez-vous sur la console des API Google.

Cette interface vous permet de gérer vos applications et vos API. Vous allez pouvoir créer votre première application. Pour ça, cliquez sur « Créer un projet ». Vous avez simplement besoin de le nommer.

creer_projet

Créer un Token

Une fois le projet créé, cliquez sur l’onglet « Identifiants » se trouvant sur la gauche, puis cliquez sur le bouton « Créer des identifiants ». C’est ici que vous allez pouvoir gérer vos clés d’API et les demandes d’authentifications.

Cliquez sur le bouton « Créer des identifiants » et sélectionnez « Clé d’API ». Plusieurs possibilités d’offrent à vous. Choisissez « Clé navigateur ».

identifiants

Dans l’étape suivante, vous devez nommer votre clé d’API et indiquer les URL qui seront acceptées par votre clé d’API. Cette étape est facultative, mais je vous conseille de renseigner les champs.
Pour ma part, je travaillais en local sur le port 9000. J’ai donc ajouté les URLs http://localhost:9000 et http://127.0.0.1:9000.

Une fois validé, une clé d’API est générée. Notez-la bien.

Créer un client OAuth 2.0

Pour créer notre client OAuth, la démarche est sensiblement la même que pour le token.

Toujours dans la page « Identifiants », cliquez sur le bouton « Créer un identifiant » et choisissez « ID client OAuth ». Sélectionnez « Application Web » comme type d’application, puis nommez votre client.

En plus des origines JavaScript autorisées, vous pouvez renseigner une ou plusieurs URI de redirection autorisées. J’ai également ajouté http://localhost:9000 et http://127.0.0.1:9000.

Vous n’avez plus qu’à valider pour obtenir votre client ID.

Vous êtes maintenant prêt à utiliser l’authentification OAuth 2.0 de Google dans Angular.js !

Un factory pour Angular.js

Pour utiliser l’authentification OAuth 2.0 de Google, j’ai créé un factory. Il suffit de l’injecter dans n’importe quel contrôleur pour pouvoir l’utiliser, ce qui rend très flexible son utilisation.

Plutôt que de grandes explications, voici le code:

'use strict';

/**
 * @ngdoc service
 * @name erpApp.googleAuth
 * @description
 * # googleAuth
 * Factory in the erpApp.
 */
angular.module('erpApp')
  .factory('googleAuth', function ($http) {

    /* ---------------------------------------------------------------------------------------------------------------------------- *\
     * GOOGLE DATAS
    \* ---------------------------------------------------------------------------------------------------------------------------- */
      let clientId  = 'YOUR_CLIENT_ID';
      let apiKey    = 'YOUR_API';
      let scopes    = [
        'https://www.googleapis.com/auth/plus.me', 
        'https://www.googleapis.com/auth/gmail.readonly'
      ];
    /* ---------------------------------------------------------------------------------------------------------------------------- */


    /* ---------------------------------------------------------------------------------------------------------------------------- *\
     * EVENTS
    \* ---------------------------------------------------------------------------------------------------------------------------- */
      // Define events
      let googlePlusEvent = new Event('googlePlusReady');
      let gmailEvent      = new Event('gmailReady');
      let userDatasEvent  = new Event('userDatasReady');

      // Events listenners
      window.addEventListener('googlePlusReady', function (e) {
        console.log('Google+ event fired !');
        googlePlusEventFired = true;

        if( gmailEventFired ) {
          // Dispatch the event.
          window.dispatchEvent(userDatasEvent);
        }
      }, false);

      window.addEventListener('gmailReady', function (e) {
        console.log('Gmail event fired !');
        gmailEventFired = true;

        if( googlePlusEventFired ) {
          // Dispatch the event.
          window.dispatchEvent(userDatasEvent);
        }
      }, false);        

      // Flags
      let googlePlusEventFired  = false;
      let gmailEventFired       = false;    
    /* ---------------------------------------------------------------------------------------------------------------------------- */ 


    let googleFuntions = {
      /* ---------------------------------------------------------------------------------------------------------------------------- *\
       * GOOGLE USER DATAS
      \* ---------------------------------------------------------------------------------------------------------------------------- */
        familyName: null,
        givenName:  null,
        email:      null,
        googleId:   null,
        avatar:     null,
        token:      null,
      /* ---------------------------------------------------------------------------------------------------------------------------- */

      handleClientLoad: function() {
        gapi.client.setApiKey(apiKey);
        window.setTimeout(googleFuntions.checkAuth,1);
      },

      checkAuth: function() {       
        gapi.auth.authorize( {client_id: clientId, scope: scopes, immediate: true}, googleFuntions.handleAuthResult );
      },

      handleAuthResult: function(authResult) {
        console.log("Authentification results:",authResult);

        // If user was already auth
        if (authResult && !authResult.error) {
          googleFuntions.token = authResult.access_token;

          googleFuntions.makeApiCall();
        } 
        else {
          // User not already connect, ask him to connect his Google Account
          gapi.auth.authorize({client_id: clientId, scope: scopes, immediate: false}, googleFuntions.handleAuthResult);
          return false;
        }        
      },

      makeApiCall: function() {
        gapi.client.load('plus', 'v1').then(function() {

          let request = gapi.client.plus.people.get({
            'userId': 'me'
          });

          request.then(function(resp) {
            console.log("API response:",resp);            

            googleFuntions.googleId = resp.result.id;
            googleFuntions.familyName = resp.result.name.familyName;
            googleFuntions.givenName = resp.result.name.givenName;
            googleFuntions.avatar   = resp.result.image.url;

            window.dispatchEvent(googlePlusEvent);
          }, function(reason) {
            console.log('Error: ' + reason.result.error.message);
          });
        });

        gapi.client.load('gmail', 'v1').then(
          function(){
            let request = gapi.client.gmail.users.getProfile({
              'userId': 'me'
            });

            request.then(function(resp) {              
              console.log("Gmail response:",resp);              

              googleFuntions.email = resp.result.emailAddress;

              window.dispatchEvent(gmailEvent);
            }, function(reason) {
              console.log('Error: ' + reason.result.error.message);
            });
          }
        );
      },

      checkToken: function(token) {
        if ( typeof token !== 'string' ) {
          window.alert('Error: Token must be a string !');
        }
        else {
          $http.get('https://www.googleapis.com/oauth2/v1/tokeninfo?access_token='+string).then(
            // Success
            function( results ) {
              console.log('Succes !',results);
            },
            // Error
            function( results ) {
              console.log('Error !',results);
            }
          );
        }
      }
    };

    return googleFuntions;
  });

Et ensuite ?

À l’aide de ce factory, vous récupérez l’ID Google du client, son nom, son prénom et son adresse mail. Un token, valide 3600 secondes, est également généré.

Vous aurez sans doute besoin de stocker ces informations en base de données. Pour cela, une API Rest sera nécessaire, interrogeable à l’aide du service $http d’Angular.js.

N’hésitez pas à poser vos questions en commentaire ! 

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *