Home

Module npm sesalab-sso

Ce module permet de faire du sso entre deux applis node, il a été conçu pour propager une authentification
de sesalab vers des sesatheques, mais peut servir pour coupler des sesatheque dont une sert de serveur
d'authentification aux autres, ou plus généralement de sso entre deux applis node+lassi.

Dans ce qui suit, quand ce n'est pas précisé il faut entendre par "serveur" le nodeJs de l'appli serveur d'authentification, et par "client" le nodeJs de l'appli cliente du sso, qui récupère les users de ce serveur d'authentification.

Dans l'usage sesalab-sesatheque, un sesalab est serveur et une sesatheque cliente, et cela concerne uniquement les comptes prof (formateurs ou sésaprof), car les élèves ne sont pas authentifiés sur les sesathèques.

Principe

Mode push

L'utilisateur se connecte sur sesalab, l'appli sesalab lance une connexion vers une sesatheque

  • connexion OK sur sesalab, qui met le user en session et appelle ensuite $sesalabSsoServer.loginOnClients(context, user) (c'est GET /sso/sesatheques qui fait ça, cf sesalab:sesalab-home/source/server/sso.js) qui se charge de la suite
  • création d'un ssoToken associé au user pour le passer dans l'url et que le client puisse récupérer ce user via appel direct
  • redirection http du navigateur vers une sesatheque avec du ?token=xxx&retour=urlRetourVersSesalab
  • le serveur de la sesatheque appelle celui de sesalab avec le token, qui répond avec un objet User, que la sesathèque peut créer si elle le connait pas et créer la session. Si y'avait pas encore de session, la sesatheque se rappelle elle-même en ajoutant cookied=1 (pour forcer express a créer la session)
  • redirection http du navigateur vers urlRetourVersSesalab avec le token et l'ajout d'un param success=true
  • le serveur sesalab efface le token et redirige vers une autre sesatheque ou son appli en mode authentifié

La cinématique est donc, à partir d'un login sesalab OK ("=>" signifie redirect 302)

  • sesalab:/sso/sesatheques (loginOnClients fait un $cache.set(token, user) puis déclenche les redirect)
    =>

Mode pull

  • depuis une sesathèque, l'utilisateur demande une connexion sur sesalab, => appel login
    sesalab avec une url de redirection une fois login OK, cette url sesathèque est appelée avec un ticket en paramètre, ticket qui permet de récupérer le user.

Header Authorization

Le SSO que propose ce module consiste à ouvrir une session sur l'appli cliente (à priori sesatheque) en transportant l'information utilisateur (avec création éventuelle de compte sur l'appli cliente). La redirection permet d'ouvrir une "vraie" session sans avoir de souci de cross-domain.

Outre la gestion unique du compte et la saisie unique du login / pass, cela permet ensuite d'ouvrir des pages authentifiées de l'appli cliente en iframe de l'appli serveur (à priori sesalab), et que cela fonctionne indépendamment des réglages des navigateurs (il faut quand même évidemment autoriser les cookies de session).

Pour que le navigateur de l'appli serveur puisse faire des appels ajax vers l'appli cliente, la session ne suffit pas toujours, parce que l'option withCredentials des appels ajax ne fonctionne pas toujours si les cookies tiers sont refusés (le cookie n'est pas envoyé par safari, les autres avec un refus de cookies tiers refusent le setCookie mais avec le withCredentials ils envoient des cookies existants).

Il faut donc ajouter un token d'authentification, dans le header Authorization prévu pour cet usage (une requête ayant ce header n'est jamais mise en cache par un intermédiaire, comme pour les cookies). Des solutions normées existent (Oauth2, plus généralement les bearer token, ou json web token), où le token contient les informations de droits et est signé.
Ici, les deux parties connaissent l'utilisateur et ses droits (il a une session ouverte sur chaque appli, indispensable pour l'affichage d'iframes), donc on va se contenter d'un token permettant simplement de faire le rapprochement avec la session existante.
Ce token est créé sur l'appli cliente lors du login et passé à l'appli serveur lors de l'appel du validate (le lien avec la session utilisateur est créé avec la mise en session du user, à la réception des infos sur le user renvoyées par le serveur d'authentification).
Il est alors mis dans la session de l'utilisateur coté serveur, charge à l'appli de le transmettre coté front pour les appels ajax.

Ce module gère la génération de ces authToken, leur mise en session, la correspondance avec les sessions et l'ouverture des sessions sur les appels de l'api (via un middleware qui génère le cookie connect.sid juste avant que le module express-session ne l'utilise).

Si un jour le besoin de gestion de token d'authentification sans avoir de session se faisait sentir il faudrait ajouter sur l'api cliente une façon de récupérer les droits d'après ce token, comme dans un usage de berear token ou de jwt.
Ça semble aujourd'hui inutile sur une sesathèque utilisée depuis un sesalab (peu de requêtes sur l'api, bcp d'iframes, et avec ce système un service $accessControl, utilisant la session, identique pour l'api et les pages).

Usage

Le module ne gère aucune vue.

Ses contrôleurs ne font que des redirections ou des appels de context.next(error)

Tout le reste doit être géré dans l'appli qui inclue ce module

Cf configuration (il y a des exemples dans le dossier config.example)

coté serveur d'authentification

  • renseigner la liste des clients dans la config (pour sesalab, c'est construit automatiquement d'après les sésathèques déclarées en config, cf sesalab:config/index.js)
  • gérer un form de login (appel de $sesalabSso.loginOnClients dans le controleur qui valide le form,
    qui doit être une page et pas un appel ajax car il va rediriger)
  • gérer 3 pages
    • login : pour qu'un client puisse réclamer un login en fournissant une url de retour
      (il faudra y retourner si user/pass ok)
    • logout : appeler en ajax les urls des clients chez qui on avait propagé un login
      (fournies par $sesalabSsoServer.getClientsLogoutUrl())
    • error : une page qui affiche une erreur chez un client (erreur fournie par ?error=…)

Pour un login initié sur l'appli serveur, après le login réussi (et mise en session),
pour propager ce login, lancer

$sesalabSsoServer.loginOnClients(context, user)
// pour limiter à certains clients, appeler plutôt
$sesalabSsoServer.loginOnClients(context, user, ['baseUrlClient1', 'baseUrlClient2'])
// cet appel se terminera par une redirection vers afterClientsPage,
// sauf si on a une erreur ici (on appelera context.next(error) depuis un de nos contrôleurs)

// sur l'url afterClientsPage, l'appel de $sesalabSso.loginOnClientsResults(context) renverra la liste
// des logins ok|KO

Cette commande va rediriger le navigateur (plusieurs fois s'il y a plusieurs clients),
sauf en cas de problème où il y aura un appel de context.next(error) (depuis un contrôleur de sesalabSso)

Une fois connecté sur la liste des clients de settings.components.sesalabSso.clients, le module
redirigera vers config.components.sesalabSso.afterClients ou config.components.sesalabSso.errorUrl (avec un
paramètre ?error=Un+message)

Pour la déconnexion chez les clients, il faut faire une page contenant du js (navigateur) qui fera des appels ajax
vers les différents settings.components.sesalabSso.clients[i].logout

coté client

  • fournir la liste des serveurs d'authentification dans config.components.sesalabSso.authServers
    (appeler plus tard $sesalabSso.setLoginCallback si un service est nécessaire, car il n'est probablement pas dispo
    dans le fichier de configuration, mais il le sera dans le mainComponent.config de l'appli)
  • (@todo) pour s'authentifier depuis le client…

Couplage

Pour réduire le couplage entre ce composant et sesalab / sesatheque, tout ce qui est spécifique à l'un et
l'autre est mis en config, d'où les callback à préciser.

Pour les noms de propriété d'un utilisateur, cf component/User.js
(l'utilisateur de départ reste accessible dans user.original)

Note

Bien que le module soit prévu pour fonctionner avec de multiples clients et multiples serveurs, seule
la configuration avec un sesalab serveur et deux sesatheque cliente a été testée.

Détails

Actions coté client

  • askLogin : redirige vers le login du serveur qui rappellera le login du client avec le user après authentification
  • askLogout : déconnecte localement et redirige vers la déconnexion du serveur (qui rappellera logoutFromRemote)
  • login : sur retour du serveur avec un ticket, appelle le validate et récupère le user qu'il loggue localement
  • logout : sur demande du serveur, on déconnecte localement (réponse en json)

Action coté serveur

Exemple

Dans Sesalab, après authentification formateur et mise en session

  • GET /sso/sesatheque qui va simplement appeler $sesalabSsoServer.loginOnClients(context, user)
  • loginOnClients initialise une liste de clients avec un token chacun (le authBundle)