Passeport pour le SSO

Gérer une authentification en Node.js n'est pas aussi évident qu'avec PHP. Il existe plusieurs façons de reproduire le fonctionnement d'une session liée à un cookie mais il vaut mieux dès le départ partir sur une solution où les données de session sont stockées dans une base de données. Après tout, si on choisit Node.js, c'est pour être scalable !

Et tout ça pour dire que lors de mes recherches de modules permettant de gérer tout ça, j'ai découvert Passport, un module qui permet de gérer toutes les authentifications possible pour votre app. C'est extremement simple, et on peut même l'utiliser sans avoir la moindre base de données sur son app. Jetons y un œil.

Passport

Providers

Avant de voir comment ça marche, voici la liste non exhaustive de services qui sont pris en charge :

Voilà, il y a de quoi faire. Et quand on va voir comment chacun de ces service s'implémente, on va réaliser la puissance du truc.

Implémentation

On va partir d'un login avec Google, c'est le plus simple. Donc, il faut partir d'une app Connect ou Express. Bah oui, passport est un middleware.

Commençons par créer notre app Express, les bases quoi. Le mieux, c'est de charger les modules d'abord, ça aide en général :

npm install express
npm install passport
npm install passport-google

Ensuite rédigeons index.js

var express = require('express')
  , app = express()
  , http = require('http');

http.createServer(app).listen(3000);

Nous allons ensuite spécifier trois routes. L'une pour rediriger l'utilisateur vers le SSO de Google, l'autre en tant que callback et la dernière qui permettra à l'utilisateur de se délogguer :

var express = require('express')
  , app = express()
  , http = require('http')
  , passport = require('passport');

app.get('/auth/google', passport.authenticate('google'));
app.get('/auth/google/return', passport.authenticate('google', {
    successRedirect: '/',
    failureRedirect: '/login',
    failureFlash: true
}));

app.get('/logout', function(req, res){
  req.logout();
  res.redirect('/');
});

app.get('/', function(req, res){
    res.render('home', {
        user: req.user
    });
});

http.createServer(app).listen(3000);

Voilà la base, mais ça ne marche pas encore. Il faut encore expliquer à passport ce qu'on va faire des infos renvoyées par le SSO et générer un objet de session. On va écrire ça avant de spécifier les routes. Essayez de faire les choses proprement en le mettant dans un autre fichier tant qu'à faire.

var passport = require('passport'),
  , GoogleStrategy = require('passport-google').Strategy;

passport.use(new GoogleStrategy({
    returnURL: 'http://localhost:3000/auth/google',
    realm: 'http://localhost:3000'
},
function(identifier, profile, done)
{
    /**
     * Ici, Google nous renvoit les données
     * provenant du compte qui a été authentifié
     * Normalement, on va alors faire une requête
     * dans sa base de données pour chercher
     * un utilisateur correspondant à l'attribut `identifier`.
     * Cependant, on peut aussi uniquement renvoyer l'objet `profile` si on propose un outil sans base de données.
     */
    done(null, profile);
}));

Bon il faut aussi spéficier un serializer et un déserializer. Ça permet au système de ne garder que peu d'info en session, genre juste l'ID et d'aller récupérer le reste des données en base de données à chaque requête. Pour notre exemple, ça sera très simple :

passport.serializeUser(function(data, done) {
    done(null, data);
});

passport.deserializeUser(function(data, done) {
    done(null, data);
});

Et voilà. Il ne restera, pour constater le bon fonctionnement du truc, qu'à ajouter une route sur '/' où on rendra un template auquel on passera en paramètre l'objet req.user qui correspondra à l'objet récupéré par Google.

Et puis pour le reste, la doc est très claire.

Hadrien

Hi, I'm a french Javascript Lead Developer, Web Architect from Toulouse, France. I've worked for 12 years for many projects with YUI, AngularJS, Aurelia.io and now React and React native.

Toulouse, France https://hadrien.eu