Journal Glewlwyd, Serveur d'authentification OAuth2

Posté par  (site web personnel) . Licence CC By‑SA.
Étiquettes :
19
15
déc.
2017

Sommaire

Une fois que mon serveur domotique roulait tout seul, je cherchais comment occuper mon temps libre pendant les siestes de mon héritière.
J'avais une TODO List assez longue de services web que je voulais faire (elle l'est encore, en fait, c'est comme Sysiphe, ca ne se vide jamais…), un des problèmes que je voyais arriver c'est l'authentification.
Angharad gérait l'authentification lui-même avec user/mot de passe. Il se connectait soit à la base de données, soit à un serveur LDAP pour authentifier. C'est cool, mais je me disais que si je devais porter ce module d'authentification dans les prochains services, il fallait aussi que je porte les améliorations et corrections de bugs à chaque fois également, ca allait rendre la tâche pas drôle assez vite…

J'ai donc regardé ce qui se fait du coté des technos SSO et de délégation d'authentification, et j'ai choisi OAuth2.

J'ai pas trouvé de serveur OAuth2 déjà tout fait et facile à installer. Il y a beaucoup de libraries ou de petits programmes disponibles selon le site OAuth.net, mais rien qui me satisfasse. Donc je me suis lancé dans le projet d'en faire un. En langage C parce que j'aime ca. Je l'ai appelé Glewlwyd, du nom du chevalier de la table ronde qui était également le portier en chef de Camelot, celui qui laisse entrer ou non les gens.

C'est quoi OAuth2 ?

C'est un workflow de délégation d'authentification pour permettre à des serveurs de ressources de s'assurer qu'un utilisateur a bien le droit de se connecter à son service, sans qu'il ait à demander à l'utilisateur de s'authentifier chez lui. C'est pas mal lié aux us et coutumes du Web mais c'est assez souple pour pouvoir être utlisé dans d'autres cas de figure qu'un navigateur.

OAuth2 considère 4 rôles dans un flux d'authentification:
- Le propriétaire de la ressource: C'est l'utlisateur proprement dit qui veut accéder à ses ressources (photo, image, mails, etc.)
- Le serveur de ressource: C'est le service qui fournit lesdites ressources à son propriétaire s'il est bien sûr que c'est lui
- Le client: C'est l'application qui est utilisée par le propriétaire de la ressource pour accéder aux ressources, genre une application Web
- Le serveur d'autorisation: C'est le serveur OAuth2 proprement dit qui va vérifier l'identité de la personne qui veut se connecter ainsi que les droits qu'il réclame éventuellement

Dans le cas d'utilisation principal, l'utilisateur va exécuter le programme client, le programme client va le rediriger vers le serveur OAuth2, puis, si l'utilisateur est autorisé, il sera redirigé sur le client avec un access token ou une clé qui permettra d'obtenir des access token que le client utilisera pour authentifier chaque appel aux APIs au serveur de ressource. Lorsqu'on se connecte aux gros services type Google ou Facebook, on a un workflow similaire. Un access token a volontairement une durée de vie courte, moi j'ai mis une heure, c'est pour éviter que si un token fuite, l'usurpation d'identité puisse se faire sur une longue durée. Mais il est possible d'obtenir un nouveau access token à chaque fois qu'on le souhaite, et ce de manière transparente pour l'utilisateur, via un refresh token obtenu lui aussi auprès du serveur OAuth2 et qui a lui une durée plus longue.

Après, OAuth2 a plus de subtilités que ca, mais mon besoin principal est avant tout dans ce workflow.

Valider un token OAuth2

Comme toute bonne RFC, elle décrit une chose de amnière le plus complète possible, mais elle ne se permet pas de présupposer quelque chose qui n'est pas dans son scope. En l'occurence, la RFC OAuth2 te dit comment doit se passer la phase d'authentification ainsi que le format des données envoyées, mais comment fournir le token au serveur de ressource, elle s'en cogne. Heureusement la RFC suivante, la 6750 définit un moyen de fournir le token au serveur de ressource. En gros, tu peux le passer dans l'url, le body ou un header. C'est souvent cette dernière méthode qui est employée.

D'accord mais bon, comment le serveur de ressource il sait quele token est bon? Ben là aussi, la RFC Oauth2 dit explicitement que c'est pas son problème. Plusieurs solutions sont possibles, comme fournir une API supplémentaire sur le serveur OAuth2 qui permet au serveur de ressource de s'assurer qu'un token est valide, ou avoir des tokens signés qui permettraient de les valider juste en vérifiant la signature. C'est cette deuxième méthode que j'ai choisie pour valider un token, en utilisant des JWT, d'autant que la librairie libjwt fait ca super bien.

Authentifier un utilisateur

Pour authentifier un utilisateur, on lui demandera son nom et mot de passe, ainsi que la liste des scopes qu'il souhaite avoir. Un scope est un domaine d'autorisation que l'utlisateur souhaite avoir. Cela permet d'avoir une gestion plus fine des différents droits de l'utilisateur. Par exemple, les scopes qui existent sont: mail, photo, file et admin, et l'utilisateur user1 a le droit d'avoir accès aux scopes mail et photo. Si le client demande l'accès à trous les scopes pour user1, le serveur OAuth2 lui répondra OK, mais juste pour mail et photo, le reste il a pas accès. Dans Glewlwyd, la liste des scopes accessibles sont également écrits dans le token, donc le serveur de ressource peut (et doit) aussi valider que le token qu'on lui fournit possède bien les droits pour le ou les scopes qu'il réclame.

Oui, mais encore?

L'utilisateur peut modifier son mot de passe, et peut se faire envoyer un e-mail pour réinitialiser le mot de passe s'il l'a oublié.

Glewlwyd permet donc d'authentifier des utilisateurs qui proviennent soit de la base de données, soit d'un serveur LDAP distant.
Pour fonctionner il a besoin d'une base de données (MariaDB ou SQlite3) et c'est à peu près tout.

Je voudrais bien l'essayer

L'url du projet sur GitHub est là: Glewlwyd, mais des gens sympas m'ont aidé, et il y a maintenant un package Docker, et il est aussi disponible sur Debian testing ou encore Ubuntu maintenant, et j'en suis très content!

C'est du GPL V3 pour la partie serveur, écrite en C, et du MIT pour la partie client web, écrite en React JS.

Pourquoi t'as choisi d'implémenter un serveur OAuth2 plutôt que XYZ?

Je ne suis pas expert dans ce domaine, mais je voulais m'y mettre tout seul et évidemment de façon libre. J'ai regardé quels étaient les défauts et les reproches qu'on peut faire à OAuth2 et j'ai pesé le pour et le contre. Au final, c'était le protocole le plus à ma portée et le plus fiable et sécurisé que j'ai pu trouver. Je sais que d'autres protocole existent et sont mille fois mieux sur plein d'aspects et pour plein de raisons, mais soit ca utilisait des mots que je comprenais pas, soit ca disait que y'a pas de specs.

Tu peux rajouter la fonctionnalité ABC?

J'ai déjà eu plein de retours, proportionnelement au fait que c'est un projet personnel j'entends, des rapports de bugs et des pull request, et aussi plein de conversations super intéressantes avec des gens.
Mon avis est que ca vaut la peine de rajouter des fonctionnalités comme TOTP, différents backends pour authentifier les utilisateurs, voire gérer des protocoles autres que OAuth2.

Mais dans ce cas je partirai sur une architecture modulable, car pour l'instant c'est monolithique, et plus on rajoute de fonctionnalités comme ca, plus on risque des effets de bord.
J'ai gouté aux libraries qui se chargent lors de l'exécution et j'ai trouvé ca très bon, donc j'en referai. Ca sera pour la prochaine version!

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.