28 janvier 2014

Jouons un peu avec securesocial

L'équipe du BreizhCamp est en train de dépoussiérer l'appli Call-for-Papers développée l'an dernier. L'objectif premier est bien sur l'ouverture prochaine du CFP 2014, mais aussi l'hébergement à moyen termes d'un Call-for-Paper "as-a-Service", opensource et gratuit pour les tous les organisateurs de conférences. 

L'idée vient évidemment de ce que je bosse chez CloudBees (ce qui nous permet d'avoir des tarifs d'hébergement imbattables !) et que le développement d'un SaaS multi-tenant est un sujet qui m'intéresse énormément. 

L'idée vient aussi de ce que je suis moi-même speaker, et que j'en ai marre de copier/coller ma bio d'un CFP à l'autre. Avec un seul service sur lequel j'aurais mon profil, je pourrais postuler plus confortablement :)

Bref, ménage de printemps dans le code, qui est fonctionnel mais ... mérite quelques améliorations :) En gros, l'appli a été finalisée l'an dernier PENDANT le call-for-papers, il y avait donc pas mal de trucs plus ou moins bien finis.


L'appli est en Play2 / Java (l'équipe n'est pas très friande de code Scala) et l'UI en AngularJS. Nous avons donc naturellement confié l'authentification à la librairie SecureSocial. Vous pouvez donc vous identifier via les services OAuth github, google, etc.

OAuth

OAuth est un protocole d'authentification qui permet de fournir à une appli l'accès à vos données hébergées par une autre appli, sans pour autant lui donner votre mot de passe. L'application "cliente" vous renvoie vers l'applicable "cible", qui affiche une demande de confirmation, indiquant les droits demandés. Un callback fournit alors à l'application cliente un token pour accéder aux infos demanders. 

La sécurité d'OAuth est basée sur l'enregistrement de l'appli cliente chez le fournisseur de données.Le client dispose un ID+Secret unique qu'il utilise pour demander au fournisseur l'accès aux données - fournisseur qui demande l'autorisation de l'utilisateur - avant d'appeler le callback redirect_url.  



Je ne vais pas vous détailler plus OAuth, complètement pris en charge par securesocial, ce qu'il faut retenir c'est cette redirect_url vers laquelle vous ramène le provider après confirmation de l'accès. Ce callback doit être sur le domaine prévu, ce qui évite une utilisation frauduleuse, en complément de l'ID+Secret.

SecureSocial

SecureSocial est une libraire bien pensée, offrant des API Scala et Java, ce qui nous arrange bien :)

Un problème qui avait émergé dès l'an dernier, est que securesocial nous fournit comme identifiant d'utilisateur l'ID défini par le provider OAuth. Cela veut dire que Jean Dupont identifié par github n'est pas la même personne que Jean Dupont identifié par google. Par ailleurs, l'API de twitter ne permet pas d'obtenir l'email de l'utilisateur - et c'est volontaire

Première évolution, l'email devient la clé d'un utilisateur, ce qui autorise l'identification multi provider, et bien sur Twitter passe à la trappe (nous pourrions le rétablir, sous réserver d'obliger l'utilisateur à saisir son email et à le confirmer ... bref ce n'est pas du tout indispensable).

Seconde évolution, j'ai ajouté l'authentification via linkedin. J'ai galéré un moment, avant de comprendre que le provider OpenSocial ne prend pas le scope email chargé de demander l'email utilisateur. J'ai donc du définir ma propre implémentation, l'occasion aussi de remettre un peu le nez dans du Scala, j'aime bien de temps en temps, ça me change de Jenkins...

Détail amusant (?), l'implem' OAuth de LinkedIn ne contrôle pas du tout le domaine utilisé par la redirect_url, ce qui permet de tester même depuis localhost:9000 - de là à se demander si c'est très sécurisé tout ça ...

Troisième évolution, dans le but de faire de notre Call-for-Papers un SaaS, il faut gérer des sous-domaines (breizhcamp.call-for-papers.io, javaone.call-for-papers.io, etc). Vous voyez rapidement le problème, chaque conférence ayant un domaine propre, il faudrait un ID+Secret différent sur chaque provider OAuth. L'idée est donc que la page de login soit sur www.call-for-papers.io puis redirige vers le tenant initialement demandé.

La librairie securesocial, initialement sélectionnée pour nous simplifier la vie, nous met des battons dans les roues. J'ai dû créer une version custom de SecureAction qui rajoute deux lignes pour forcer l'hôte "www" en login, et mémoriser l'URL complète (pas juste l'URI relative au serveur) pour renvoyer l'utilisateur vers la page initialement demandée.

Pour l'instant, j'ai copié/coller du code de securesocial, je pense à plutôt me baser sur un fork, ce qui permettrait de synchroniser les évolutions upstream plus facilement (merci Git).

Bref, la vie n'est pas un long fleuve tranquille :)