The User is dead


L’authentification de mémé (enfin, mémé…) n’a plus de sens, et pourtant, on continue de la montrer en exemple dans tous les tutos :

  • Un compte utilisateur.
  • Un email.
  • Un mot de passe.

Boom, on vérifie le mot de passe (avec un peu de chance sur une version sainement hachée), on identifie l’utilisateur, et c’est plié.

C’est simple, c’est beau, ça marche.

Mais ça ne représente pas du tout la réalité.

Une des plus grosses erreurs de design de frameworks comme Django est justement la classe User.

En soit, la classe User n’est pas un problème, mais son omniprésence dans toutes les couches du framework et sa promotion au rang d’unité de référence dans les apps tierces parties ont gardé la vision de l’authentification très monolithique.

C’est que le Web n’a plus du tout la forme qu’il avait il y a 10 ans, et aujourd’hui, vous pouvez vous connecter via un service (facebook, twitter, github…), utiliser une authentification à double facteur (sms, email, yubikey…) ou avoir un compte unique qui unifie plusieurs fournisseurs (ah, le fameux compte Google qui fait gmail, youtube, calendar, play, saucisseland…).

Les gens ont plusieurs identités, plusieurs comptes, plusieurs modes d’authentification, plusieurs points d’entrées. Ce modèle ne marche plus :

USER <-- Profile 

Si vous créez un service aujourd'hui et que vous souhaitez prendre en compte cette réalité, le schéma en base de données va plutôt ressembler à un truc du genre :

                      +-------+
                      |Session|
                      +----+--+
                           |
                           |
                           |
                           |
+-----------+         +----v---+         +--------+
|  Identity | <-------+ Account| <-------+Profile |
+-----------+         +--------+         +--------+
                 +------^   ^--------+
                 |                   |
                 |                   |
                 |                   |
                 |                   |
         +-------+-----+      +------+----+
         | Credential  |      |  Service  |
         +-------------+      +-----------+

Et encore, je n'ai pas mis les permissions, les devices, les comptes multi-utilisateurs, la vérification des comptes (lien email, sms, etc), les niveaux d'offre commerciale... Ca rend le truc vraiment compliqué.

Ce qui est important ici, c'est que la notion de compte devient centrale, mais que seule l'identité est unique. Une identité peut avoir plusieurs comptes. Et un compte peut être chez votre service ou un service externe. Et l'authentification peut très bien avoir lieu ailleurs : ldap, OAuth, Mozilla Personna, Open ID...

Si votre utilisateur a un compte chez Facebook et un compte chez vous, les deux sont des comptes, avec leurs profils, reliés à la même identité, servant à vous donner des informations, permettant de se loguer... Et vous devez garder une trace de tout ça. Le fait que l'API d'un service tape sur votre serveur et l'autre non n'a en fait aucune importance pour l'utilisateur, qui utilise tout ça comme un gros blob comme si internet était un tout.

La nouvelle génération ne fait pas la différence avec un site Web et un app, ne sait pas ce qu'est une URL ou un cookie et ne veut pas rentrer trop de mots de passe, mais veut par contre utiliser 5000 services.

Ainsi on voit que les archis basées sur des microservices (hello crossbar :)) commencent à avoir la côte, faisant abstraction de la notion de "ceci est mon app, ceci est ton app" dans la structure de son projet. Bien entendu, la distinction est importante d'un point de vue humain, mais du point de vue du workflow d'authentification, non.

Par contre, forcément, ça fait beaucoup plus qu'une table User et une table Profile en base de données, et c'est pourquoi toutes les apps pluggables des frameworks actuellement ont leurs propres structures bizarres, incompatibles et torturées pour faire tenir toute cette logique. Parce qu'on code sur des frameworks qui vivent encore dans les années 2000, qui passent à votre vue un objet User en paramètre avec ses attributs username et email.

Mais ce que vous avez en face de vous, ce n'est pas un utilisateur.

C'est une session d'utilisation.

D'un service (le votre). Liée à un compte. Qui peut-être (surement) possède un profil avec un username.

Cette session s'est initiée par une authentification avec une méthode parmi 100 auprès d'un service également, lié à un compte, avec un profil. Peut-être le même service que celui qui est utilisé, peut-être pas.

Et cette authentification a pu être demandée par un utilisateur, ou encore un autre service, ou un logiciel client complètement automatiquement. Souvenez-vous : la session n'a peut-être aucun humain derrière.

Ce qui est en face de vous n'est pas un utilisateur. L'utilisateur est mort il y a des années.

18 thoughts on “The User is dead

  • Dumont Nicolas

    Bonjour, je découvre votre site via cette article qui m’intéresse fortement. Je me pose la justement en ce moment là question de l’authentification et j’avoue rejoindre vite point de vue. Cependant, il y a un point très important qu’il me semble impératif de souligner : la complexité. Oui, pour avoir testé l’authentification Facebook et Google je n’ai rien trouvé, ni documentation ni exemple suffisamment bien fait pour que cela soit simple et rapide. Donc j’en suis revenu et j’utilise mon propre service via une table User (bouuuuh). Comprenant très bien votre point de vue, je me demande si vous pouvez me conseiller une façon de faire ainsi qu’une librairie pour m’aider à revenir sur la décision (j’utilise PHP, une base MySQL et un je découvre AngularJs) merci

  • djinneo

    En fait, je n’ai jamais vraiment été très fan de l’identification par compte utilisateur sur ‘ternet, et je dois avouer que depuis la lecture de cet article, le cas user me turlupine plus que d’habitude. Honnêtement, dans nombres de cas, l’identification est très clairement superfétatoire car relevant malheureusement du réflexe marketo-pavlovien inhérent au web 2.0.

    Trop souvent, l’identification relève d’un choix imposé par le concepteur alors que d’autres solutions seraient plus pratiques pour les utilisateurs: VPN, identification par adresse MAC, géolocalisation, cookie (non permanent), facture unique, etc…, et même absence totale d’identification quand les sessions sont largement suffisantes. Sauf que les frameworks fullstack mainstream (je pense à quoi ? :-D ) disent qu’il faut créer des utilisateurs, alors c’est ce que font les utilisateurs de ces frameworks en déléguant la conception de leur appli au framework utilisé.

    Les cas où des alternatives seraient préférables au compte utilisateur sont nombreuses: l’appli à destination d’un nombre restreint de personnes qui se font confiance, les transactions commerciales ponctuelles (typiquement le tourisme, la culture, et probablement les échanges supérieurs à 513€, etc.), forums et commentaires, utilisation depuis un poste fixe, utilisation depuis un réseau local, etc…

    Je ne pense pas, non, en fait, je sais que je n’ai pas été si loin dans le détail de l’identification même si j’ai déjà réfléchi aux comptes multi-utilisateurs et aux utilisateurs multi-comptes. Certainement parce-que je n’ai jamais été tenu d’assurer une authentification par service externe, et probablement parce-que j’ai assez peu d’affinité avec les réseaux sociaux. D’ailleurs, soit dit en passant, ça m’enquiquine un tantinet de devoir renseigner une adresse mail pour poster un commentaire ici; par contre je suis bien content de ne pas avoir à saisir une captcha, ni de mot de passe. C’est pas mal cool.

    Le principe selon lequel c’est au concepteur de se plier à l’usage des utilisateurs et non aux utilisateurs de se plier à un logiciel n’est pas spontanément apparu avec la nouvelle génération. Au même titre que la nouvelle génération, les vieux schnocks sont eux aussi passablement amusés par la multiplication des identifiants qu’il leur est imposée: c’est la définition même de l’ergonomie. C’est comme le Porc-Salut, c’est marqué dessus: un mot de passe, on devrait pouvoir s’en passer. Il ne devrait être qu’un dernier recours. Et certainement pas un réflexe pavlovien.

    Ce qui se dessine, c’est l’authentification biométrique côté client, avec un back-end de repêchage d’info individuelles depuis un serveur unique normalisé pour s’interfacer avec n’importe quelle appli nécessitant une authentification. Le tout moyennant finance pour un FB.get.billing_adress(biométrie), ou un Goo.go.gadget_au paiement(biométrie). En fait, on a déjà plusieurs orteils dans ce futur là. Et c’est assurément plus de confort pour tout un chacun. Tu peux être assuré qu’un framework comme Django intégrera nativement ce genre d’API.

    L’avenir, je le vois différemment. En se concentrant sur des appli multi-user pooled. Avec des clefs qui ne seraient plus uniques, mais des clusters de clefs. Eventuellement dont une partie serait propre au client, une autre partie propre au service, et enfin une autre propre à l’application utilisée. Je ne suis pas sûr que ça marche comme ça, mais l’idée est que la signature numérique permette d’identifier l’appartenance à un groupe, mais pas l’identité du signataire.

  • 3foisv

    Oui un seul profil pour les identifier tous…

    Toutes nos infos chez un seul mega-major du web dont le business model est de faire du ble avec nos infos…

    identification biometrique, ce futur me fait juste horreur, du pain beni pour le business model dont je parle au dessus et pour la surveillance de masse gouvernemental

    Decidement l’internet c’etait mieux avant, quand c’etait un espace de libre echange.

  • Gring

    Ce qui me fait peur avec la possibilité de lier plusieurs comptes sur des services externes à une seule identité, c’est qu’on multiplie les risques de hack / vol d’identité.

    Si on laisse les utilisateurs se connecter indifféremment avec leur compte Google OU Facebook OU Twitter OU OpenId, on multiplie d’autant les risques.

    Ajouter un mot de passe supprimerait tout l’intérêt du compte externe, exiger pour certaines opérations la connexion multiple ne marche pas non plus, les comptes tombant souvent comme des dominos une fois que l’attaquant a mis la main sur le compte mail …

  • jraez

    C’est un peu ce que django-allauth essaie de corriger/apporter.

    http://django-allauth.readthedocs.org/en/latest/

    Personnellement, je l’utilise toujours, même si je n’ai besoin que d’un username/password (parce que c’est plus simple d’utiliser l’email comme login plutôt que le username aussi). Et ça me laisse la possibilité de brancher ça sur facebook/twitter/oauth et autre saucisseland quand le client final change d’avis pour la 812ième fois.

  • Ginko

    C’est quoi “crossbar” ? Wikipedia n’a pas l’air de le connaitre.

  • Ginko

    Autant pour moi, quel flemmard ! Il fallait scroller un peu plus sur ddg ! (crossbar.io, pour ceux qui se poseraient la question)

  • hugras

    article pertinent mais pourquoi critiquer django sur ce point ?

    j’ai substitué l’auth de django à celle du serveur ldap de mon entreprise sans trop d’effort via un module dédié.

    le framework propose juste un service d’auth par défaut mais qui peut dégager si besoin.

  • Sam Post author

    Parce que toutes les apps pluggables partent du principe qu’il y a un objet User très rigide ce qui amène plein de problématiques à contourner dès qu’on sort de ce use case.

  • c24b

    noob question: tout à fait d’accord mais dans ce cas uhn indice à donner ou un use case pour contourner le truc?

  • Sam Post author

    ça veut dire quoi “contourner le truc” ? Quel est ton objectif ? Quel résultat concret te permettra de dire qu’il est atteint ?

  • c24b

    L’objectif c’est un utilisateur authentifié avec un ou plusieurs mode d’authentification:

    soit UserA ==> twitter+ fb + compte onsite avec droit spécifiques et infos supp

    par exemple qui correspond à la même personne

    et pas à 2 comptes différents

    Je suppose qu’il faut surclasser le model User avec une classe Identity par exemple

    faire des vérifs en base avec un param unique et ajouter s’il existe et register form sinon?

  • Sam Post author

    Ceci n’a rien à voir avec l’article. L’article détaille un problème d’architecture, et utilise l’approche de Django comme exemple. Ce n’est PAS un article sur l’authentification multiple incluant des services tierces parties en général, qui n’est qu’un sous ensemble de cette problématique. Problématique par ailleurs résolue par des libs comme django-allauth (http://django-allauth.readthedocs.org/en/latest/) cité dans les commentaires un peu plus haut.

  • Sam Post author

    Pour l’archi c’est un problème plus compliqué, et ça dépend beaucoup de l’objectif.

    Est-ce que tu veux coupler l’auth et le reste de l’app, sont-ce des services séparés et indépendants ? L’auth fait-il la supposition d’une forme de stockage ou est-ce que c’est aussi un service séparé ?

    Ensuite, est-ce que tu dev juste pour toi, où tu fais un framework ?

    L’archi la plus flexibles serait d’avoir 3 services, un pour l’auth, un pour le stockage, et le reste. Mais ça a un coup niveau perf et en termes de couches d’abstraction à maintenir.

    En partant sur du un une session => un compte => une identité, et en supposant qu’il n’y a pas de hiérarchie d’identités, on aura donc au moins une identité centrale qui solidarise tout, et un compte avec un type, et des meta information, et un profile de compte qui lui dépend du type de compte (local, facebook, etc).

    On a des identifiants qui font passer par un backend qui va devoir retourner une identité et l’attacher à la session, sachant que la session doit être l’objet central à ce moment. Stocker ou pas la session dépend du besoin d’historique, perso je ne le fais pas.

    Si on se sent chaud, on peut rajouter une notion de point de connexion, qui est un intermédiaire entre le l’identité et le compte qui comprend des choses comme des identifiants unique (clé API, etc) ou le moyen de connexion (client, device, url de connexion…), etc. Ca peut être utile si on a une app web qui utilise ce genre d’API. C’est également le point d’entrée des web hook, c’est à dire le point d’entrée des services externes autorisés par le détenteur de l’identité. Dans ce cas, la connexion ordinaire devient une de ces opérations également, et ça rajoute une couche. C’est chiant, mais c’est plus flexible.

    Les permissions, pareillement, sont liées à un compte, ou au point de connexion et non à une identité, puisque que ce sont les permissions des actions qu’on peut faire sur ce compte/point de connexion. Le compte local devient donc un service comme les autres.

    La session permet juste un accès rapide au compte “local” pour obtenir les permissions et le profile “locaux” avec un cache pour un accès rapide.

    La partie la plus compliquée, c’est l’auth, car tout ne se passe pas par une logique identifiants = connection. Et faire un truc générique, c’est très chaud, et ça dépend du nombre de services visés, de leur nature, du degré d’intégration et des phases de la lune.

Comments are closed.

Des questions Python sans rapport avec l'article ? Posez-les sur IndexError.