# Gestion des identités

Après avoir importé et instancié le SDK, il faudra qu'il dispose d'une "identité" pour effectuer d'autres opérations.

Un utilisateur est représenté par plusieurs "identités", la plupart du temps une par appareil. Ces identités sont créées au travers du SDK. Elles peuvent être simplement gardées en mémoire volatile, ou au contraire dans une base de données locale persistante. Elles peuvent également être sauvegardées sur nos serveurs de façon sécurisée pour pouvoir les réutiliser par la suite.

Pour sauvegarder des identités sur nos serveurs, il existe deux mode de protection :

Pour une gestion manuelle de la protection d'une identité que ne couvriraient pas ces plugins, n'hésitez pas à nous contacter ou utiliser l'export manuel.

uml diagram

# Créer des identités

Un utilisateur de Seald est représenté par plusieurs "identités". Ce sont concrètement des clés privées générées localement sur l'appareil de l'utilisateur avec le SDK.

Elles sont toutes reliées entre elles par une chaîne de signature. Pour plus de détails sur notre technologie, consultez la page dédiée (opens new window).

On distingue deux cas :

  • une première identité ;
  • les identités suivantes qui y sont rattachées que l'on appelle "sous-identités".

# Première identité

Pour créer une identité pour un nouvel utilisateur, il faut utiliser la fonction sdk.initiateIdentity.

Les deux arguments nécessaires pour cette fonction sont :

  • userId : un identifiant unique de cet utilisateur pour votre application. Ce userId peut être n'importe quel string, tant qu'aucun autre utilisateur de votre application n'aura le même, et qu'il ne contient pas de caractères spéciaux ;
  • userLicenseToken : c'est un jeton de licence, autorisant la création de ce compte dans votre équipe. Un guide dédié vous expliquera comment générer ces jetons de licence.

WARNING

Si on appelle plusieurs fois initiateIdentity pour un même userId, cela va créer plusieurs identités indépendantes, et seule la dernière sera active, les autres seront désactivées.

# Sous-identités

À partir d'une identité existante d'un utilisateur on peut créer une "sous-identité" à l'aide de la fonction sdk.createSubIdentity avec une instance du SDK déjà initialisée.

Cette sous-identité pourra alors déchiffrer tout ce que peut déchiffrer l'identité initiale : lorsqu'un autre utilisateur chiffrera quelque chose pour cet utilisateur, l'identité et la sous-identité pourront toutes deux le déchiffrer.

On l'utilise typiquement pour avoir une sous-identité distincte par appareil. Par exemple, lors de la connexion de l'utilisateur à un nouvel appareil, on peut récupérer une identité "principale" protégée par mot de passe ou en 2-man rule, puis créer une nouvelle sous-identité liée à celle-ci pour cet appareil. Il est alors possible de stocker cette sous-identité en localStorage (opens new window), et de la récupérer lors de l'ouverture d'une nouvelle page, afin d'avoir des sessions persistantes.

Cette fonction rend un Buffer (opens new window) similaire à celui créé par sdk.exportIdentity et décrit dans le paragraphe Export manuel.

Vous pouvez alors importer ce Buffer (opens new window) dans une autre instance du SDK avec la fonction sdk.importIdentity, ou directement le sauvegarder sur SSKS avec les fonctions sdk.ssks2MR.saveIdentity ou sdk.ssksPassword.saveIdentity.

# Base de données locale persistante

# Principe de fonctionnement

Par défaut, le SDK Seald n'enregistre sa base de données qu'en mémoire volatile : si vous créez une identité, puis fermez et ré-ouvrez votre onglet de navigateur, l'identitée créée ne sera plus accessible et sera perdue, sauf si vous l'avez protégée sur SSKS avec un mot de passe ou en two-man-rule. Dans ce cas, bien qu'ayant une session authentifié, l'utilisateur doit retaper son mot de passe, ou s'authentifier par email, pour récupérer son identité Seald.

Pour améliorer l'expérience utilisateur, on peut utiliser une base de données locale persistante, automatiquement stockée par le SDK Seald dans le navigateur de façon sécurisée, chiffrée par une clée stockée en backend appelée databaseKey.

TIP

La base de donnée de Seald utilise nedb (opens new window), qui dans le navigateur, n'utilise pas forcément le localStorage, mais choisit (via localForage (opens new window)) la meilleure méthode de stockage entre IndexedDB, WebSQL ou localStorage selon votre navigateur.

uml diagram

Lorsque l'utilisateur va rouvrir l'application, la databaseKey sera récupérée depuis le backend via une session authentifiée, et cette clé sera utilisée pour déchiffrer l'encryptedDB.

uml diagram

# Utilisation d'une base de données persistante

Pour utiliser une base de données persistante, il suffit d'ajouter un argument databasePath pour enregistrer la base de données, et un argument databaseKey pour la chiffrer, à l initialisation du SDK.

Ici, databaseKey et sessionID doivent être générés et rendus par le serveur.

On rajoute sessionID dans le databasePath afin que celui-ci soit unique pour chaque session.

En effet, si un même utilisateur se déconnecte, puis se reconnecte, le serveur aura généré une nouvelle databaseKey. On ne peut donc pas utiliser la même base de données : il faut que la base de données soit unique pour chaque session, on fait donc varier le databasePath en fonction du sessionID.

TIP

Contrairement aux modes de protection de l'identité sur SSKS, ici le fonctionnement est identique que vous vouliez créer une nouvelle identité et la stocker dans une base de donnée persistante, ou au contraire en récupérer une existante.

Vous trouverez plus de détails, notamment sur ce que vous devez implémenter coté backend, sur la page du projet exemple à propos de la base de données persistante.

# Protection par mot de passe

La protection par mot de passe de l'identité se fonde sur une dérivation d'un mot de passe connu de l'utilisateur en une clé symétrique que l'on utilise pour chiffrer et déchiffrer l'identité.

WARNING

Une fonctionnalité de type "mot de passe oublié" n'est pas implémentable avec cette méthode sans perte de donnée. Si elle est nécessaire dans votre application, utilisez plutôt la protection en 2-man-rule.

# Principe de fonctionnement

Cette identité chiffrée est ensuite stockée sur un service dédié nommé Seald Secure Key Storage ou SSKS.

La protection par mot de passe fonctionne de la façon suivante :

uml diagram

Et la récupération de la façon suivante :

uml diagram

Le plugin @seald-io/sdk-plugin-ssks-password permet d'effectuer ces opérations automatiquement.

TIP

Plus précisément, le mot de passe de l'utilisateur est dérivé deux fois :

  • une première fois en le combinant avec le userId et le appId pour donner une "clé de stockage" qui n'est pas secrète ;
  • une deuxième fois en le combinant avec un "sel" aléatoire, le userId et le appId pour donner une "clé de chiffrement" qui elle est secrète.

L'identité est chiffrée avec la "clé de chiffrement". Puis, sont envoyés à SSKS :

  • la "clé de stockage" ;
  • le "sel" concaténé avec l'identité chiffrée.

L'identité est récupérée en envoyant à SSKS la clé de stockage qui répond le sel concaténé avec l'identité chiffrée. Le sel est alors utilisé pour obtenir la clé de chiffrement qui est à son tour utilisée pour déchiffrer l'identité.

# Sauvegarder l'identité

Pour utiliser cette methode de stockage, il faut ajouter le plugin @seald-io/sdk-plugin-ssks-password lors de l'instanciation du SDK, puis, après la création de l'identité, la stocker sur SSKS à l'aide de la fonction sdk.ssksPassword.saveIdentity.

L'identité est désormais sauvegardée.

TIP

Vous pouvez aussi sauvegarder une autre sous-identité, plutôt que l'identité de l'instance actuelle du SDK :

# Récupérer l'identité

Pour récupérer l'identité à la connexion, il faut appeler la fonction sdk.ssksPassword.retrieveIdentity, avec les mêmes arguments userId et password que ceux utilisés lors de la sauvegarde de l'identité

L'instance du SDK est désormais prête à être utilisée !

# Changement de mot de passe

Lorsque l'utilisateur change de mot de passe, vous pouvez utiliser la fonction sdk.ssksPassword.changeIdentityPassword pour que l'identité soit stockée avec le nouveau mot de passe et supprimer celle stockée avec l'ancien.

Le mot de passe est désormais changé et seul le nouveau peut être utilisé.

TIP

Si l'ancien mot de passe n'est pas connu (mot de passe oublié typiquement), ce plugin ne permet pas de récupérer l'identité. Si la fonctionnalité de "mot de passe oublié" est nécessaire dans votre application, utilisez plutôt la protection en 2-man-rule.

WARNING

Cette opération ne change pas les clés de l'identité elle-même, elle ne fait que la re-protéger avec un nouveau mot de passe. Si l'ancien mot de passe est considéré comme compromis il faut aussi créer une nouvelle identité et révoquer l'ancienne.

# Mot de passe utilisé aussi pour l'authentification

Dans le cas où vous voudriez utiliser le même mot de passe pour protéger l'identité que pour l'authentification, celui-ci ne peut être transmis au serveur tel quel, sinon le serveur pourrait alors reconstituer la clé de l'utilisateur, brisant ainsi le chiffrement de bout-en-bout.

Il faut alors modifier les étapes de connexion et de création de compte pour introduire à une pré-dérivation du mot de passe à l'aide de fonctions de dérivation sécurisées de sorte que votre serveur n'ait jamais accès au mot de passe brut, mais seulement à une version dérivée.

Vous trouverez un exemple d'implémentation ici.

TIP

Dans le cas où vous ne pourriez pas modifier la fonction d'authentification (par exemple en cas d'authentification fédérée), il faudrait alors utiliser un second mot de passe ou un autre mode de protection de l'identité comme le 2-man rule.

# Protection en 2-man-rule

Le mode de protection en 2-man-rule consiste à "couper" en deux l'identité à protéger et de placer une moitié sur le back-end de l'application soumis à authentification, et l'autre moitié sur Seald Secure Key Storage (SSKS) soumis à une authentification indépendante.

L'objectif est qu'un utilisateur ait "le droit" de perdre tous ses mots de passe et tous ses appareils sans perte de donnée. La contrepartie est que ce mode n'est pas strictement "de bout-en-bout".

Il est nécessaire d'implémenter côté serveur le stockage d'un secret stocké et remis contre authentification, ainsi qu'une interaction avec SSKS.

# Principe de fonctionnement

Plus concrètement, pour protéger une identité, le protocole est le suivant (dans le cas classique) :

uml diagram

TIP

Dans le cas où une identité a déjà été stockée sur SSKS avec cette adresse email, le serveur SSKS demandera alors une authentification par challenge email, similaire à celle décrite dans la section suivante à propos de la récupération d'une identité, avant de permettre le stockage d'une nouvelle identité.

Pour plus de détails, référez vous à la page Intégrer le 2-man-rule sur votre backend.

Pour récupérer une identité, on procède de la façon suivante :

uml diagram

Ainsi si le backend est compromis, la encryptedIdentity stockée sur SSKS ne le sera pas, et le backend ne peut pas simuler une authentification auprès de SSKS.

Inversement si SSKS est compromis, la clé twoManRuleKey ne le sera pas et SSKS ne peut pas simuler une authentification au backend.

WARNING

Ce mode n'est pas strictement de bout-en-bout dans le sens où la clé permettant le déchiffrement des données d'un utilisateur peut être récupérée sans le consentement de l'utilisateur en réunissant twoManRuleKey sur SSKS et encryptedIdentity sur le backend.

TIP

Seald prend les précautions suivantes pour assurer la confidentialité des données stockées sur SSKS :

  • utilisation de serveurs non soumis au Cloud Act (opens new window) (chez OVH (opens new window) en France) pour que le mode de protection par 2-man rule soit robuste dans le cadre d'une utilisation d'hébergeurs soumis à l'extra-territorialité du droit américain pour le backend ;
  • sur-chiffrement au repos des champs sensibles (encryptedIdentity) ;
  • hachage et salage des adresses e-mail des utilisateurs auxquelles sont associées les encryptedIdentity, de sorte qu'elles ne soient connues que lors de l'exécution. Ainsi, une compromission à froid de SSKS ne permet pas d'associer les encryptedIdentity aux adresses e-mail.

# Protection d'une identité

Pour utiliser cette methode de protection, il faut installer, importer & ajouter lors de l'instanciation du SDK le plugin @seald-io/sdk-plugin-ssks-2mr.

Votre serveur applicatif doit ensuite :

  • créer un nouvel utilisateur sur SSKS en donnant son adresse email ;
  • générer une session SSKS pour celui-ci ;
  • générer une clé secrète twoManRuleKey pour cet utilisateur (une chaine de caractère aléatoire suffisamment robuste, par exemple un UUID serait un bon choix)
  • transmettre le sessionId et la clé secrète twoManRuleKey au front-end que nous symboliserons par API.getSSKSSession() dans l'exemple.

L'utilisateur peut alors stocker son identité à l'aide de la fonction sdk.ssks2MR.saveIdentity.

Vous trouverez la documentation de l'API de SSKS pour votre serveur applicatif ici.

L'identité est sauvegardée.

TIP

Vous pouvez aussi sauvegarder une autre sous-identité, plutôt que l'identité de l'instance actuelle du SDK :

TIP

Le serveur SSKS ne peut alors pas accéder à l'identité de l'utilisateur, car il n'a pas accès à la clé secrète twoManRuleKey gardée par votre serveur. Réciproquement, votre serveur applicatif ne peut pas y accéder non plus car pour récupérer une identité il faut un challenge envoyé par email, qu'il ne reçoit jamais et ne peut pas forger.

# Récupération d'une identité

Ensuite, lors d'une nouvelle instanciation du SDK, votre backend doit de la même manière générer une session pour l'utilisateur, et transmettre le sessionId et la clé twoManRuleKey au front-end. Le serveur SSKS envoie alors un email contenant un challenge (valable 6h) à l'adresse email de l'utilisateur.

Il faut ensuite appeler la fonction sdk.ssks2MR.retrieveIdentity avec les arguments userId, sessionId, email, challenge et twoManRuleKey.

Votre instance du SDK est alors prête à être utilisée !

# Export manuel

Il est également possible de gérer le stockage d'une identité manuellement.

Pour ceci, vous pouvez utiliser les fonctions sdk.exportIdentity, et sdk.importIdentity.

sdk.exportIdentity vous permet d'exporter un Buffer (opens new window)contenant l'identité courante de l'instance SDK.

Réciproquement, sdk.importIdentity importe un Buffer (opens new window) d'identité dans l'instance SDK afin de la rendre fonctionnelle.

WARNING

Il est alors à votre charge de vous assurer de la sécurité de ces exports d'identités. Cette sécurité est bien entendue capitale pour assurer la confidentialité des données chiffrées pour cet utilisateur.