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 modes de protection :
- par mot de passe avec le plugin
@seald-io/sdk-plugin-ssks-password
; - en two-man rule avec le plugin
@seald-io/sdk-plugin-ssks-2mr
.
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.
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.
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
.
Cette fonction nécessite un JSON Web Token. Un guide dédié vous expliquera comment générer ces JWT de licence.
const seald = SealdSDK({ appId, apiURL, plugins: [SealdSDKPluginSSKSPassword(keyStorageURL)] })
await seald.initialize()
// Créer son identité Seald
await seald.initiateIdentity({ signupJWT: 'signup-jwt-value' })
Identifiant personalisé
Il est possible d'ajouter un identifiant personnalisé, appelé connector
, au format string, via l'utilisation d'un JWT. Ceci peut être fait lors de la création d'une identité, ou plus tard grâce à la fonction sdk.pushJwt
.
Le connector
doit être au format ${IDENTIFIANT}@${APP_ID}
. Le type du connector
doit toujours être 'AP'
.
Une identité peut avoir plusieurs identifiants. L'ajout d'un identifiant personnalisé se fait via la clé connector_add
définie dans cette documentation.
TIP
L'ancien nom, aujourd'hui déprécié, de cette fonctionnalité, était userId
. Le userId
correspondait à la partie IDENTIFIANT
du format de connector
décrit ci-dessus, sans le @${APP_ID}
.
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, et de la récupérer lors de l'ouverture d'une nouvelle page, afin d'avoir des sessions persistantes.
const seald = SealdSDK({ appId, apiURL, plugins: [SealdSDKPluginSSKSPassword(keyStorageURL)] })
await seald.initialize()
// Créer son identité Seald
await seald.initiateIdentity({ signupJWT: 'signup-jwt-value' })
// Créer une sous-identité
const { subIdentity } = await seald.createSubIdentity()
Cette fonction rend un Buffer
similaire à celui créé par sdk.exportIdentity
et décrit dans le paragraphe Export manuel.
TIP
Pour accélérer la création de sous-identités, vous pouvez pré-générer les clés privées en appelant la fonction seald.preGenerateIdentityKeys()
à l'avance.
Exemples
// Pré-générer 1 clé
sdk.preGenerateIdentityKeys(1)
/*
Faire d'autres choses, pour laisser le temps à la pré-génération de clés de
se faire en tâche de fond
*/
// Créer une sous-identité de façon accélérée
const { identity } = await seald.createSubIdentity()
Vous pouvez alors importer ce Buffer
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
.
WARNING
Avoir un très grand nombre de sous-identités pour le même utilisateur (plusieurs dizaines) peut ralentir les opérations concernant cet utilisateur, que ce soit la création de nouvelle sous-identité ou même simplement la création d'une session de chiffrement dont il est destinataire.
Dans le cas où un utilisateur aurait un très grand nombre d'appareils, ou se reconnecterait très régulièrement, nous vous conseillons de ne pas créer une sous-identité distincte à chaque connexion, mais simplement utiliser directement l'identité stockée sur SSKS.
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é 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ée, 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
, qui dans le navigateur, n'utilise pas forcément le localStorage, mais choisit (via localForage
) la meilleure méthode de stockage entre IndexedDB, WebSQL ou localStorage selon votre navigateur.
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
.
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.
Le serveur peut soit utiliser une unique databaseKey
par utilisateur pour tous ses appareils, soit au contraire utiliser une databaseKey
différente par session. Utiliser une databaseKey
différente par session est un peu plus sécurisé, mais implique un modèle de donnée un peu plus compliqué. Nous supposerons dans la suite une databaseKey
différente par session.
const seald = SealdSDK({
appId,
apiURL,
databaseKey,
databasePath: `seald-guide-session-${sessionID}`
})
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
Vous pouvez aussi remplacer databaseKey
par databaseRawKey
.
Attention, databaseRawKey
doit absolument être un string représentant l'encodage Base64 d'un buffer cryptographiquement aléatoire de 64 octets.
Techniquement, ceci permet d'éviter que databaseKey
soit dérivé avec scrypt
, ce qui améliore la vitesse de l'instanciation.
Si vous avez le moindre doute, utilisez plutôt databaseKey
.
Exemples
Utilisation de databaseRawKey
:
// front-end
const seald = SealdSDK({
appId,
apiURL,
databaseRawKey: 'uz0BqYCF6IzefVHd8VzvbOZVp12GTMFD2L+UwCGYiRbKhGymgwG5HMSGfNiDt37h5FaTMKHYaqCcGTtH2ZVCzw=='
databasePath: `seald-guide-session-${sessionID}`
})
Génération d'une databaseRawKey
adéquate :
// back-end
const crypto = require('crypto')
const util = require('util')
const randomBytes = util.promisify(crypto.randomBytes)
const rawKeyBuffer = await randomBytes(64)
// Ce format est strict : cela doit être exactement 64 octets, encodés en Base64
// et provenir d'une source d'aléa cryptographiquement sûre
const rawKey = rawKeyBuffer.toString('base64')
WARNING
Si databasePath
est défini, mais que databaseKey
(ou databaseRawKey
) ne l'est pas, le SDK utilisera une clé de chiffrement fixe pour la base de données (dérivée du appId
), ce qui est une mauvaise pratique et peut représenter un risque mineur de sécurité.
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 SDK Key Storage ou SSKS.
La protection par mot de passe fonctionne de la façon suivante :
Et la récupération de la façon suivante :
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 leappId
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 leappId
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
.
const seald = SealdSDK({ appId, apiURL, plugins: [SealdSDKPluginSSKSPassword(keyStorageURL)] })
await seald.initialize()
// Créer son identité Seald
await seald.initiateIdentity({ signupJWT: 'signup-jwt-value' })
// Sauvegarder l'identité Seald, chiffrée par le mot de passe, sur le serveur SSKS
await seald.ssksPassword.saveIdentity({
userId: 'myUserId',
password: 'user-known-secret-password'
})
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 :
// Créer une sous-identité
const { identity: mySubIdentity } = await seald.createSubIdentity()
// Sauvegarder la nouvelle identité Seald, chiffrée par le mot de passe, sur le serveur SSKS
await seald.ssksPassword.saveIdentity({
userId: 'myUserId',
password: 'user-known-secret-password',
identity: mySubIdentity
})
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é
TIP
Si vous utilisez plusieurs fois un mot de passe incorrect, le serveur peut limiter vos requêtes. Dans ce cas, vous recevrez le message d'erreur suivant : Request throttled, retry after {N}s
(ce qui signifie Requête bloquée, réessayer après {N}s
), où {N}
est le nombre de secondes pendant lesquelles vous ne pouvez pas réessayer.
const seald = SealdSDK({ appId, apiURL, plugins: [SealdSDKPluginSSKSPassword(keyStorageURL)] })
await seald.initialize()
// Récupérer l'identité Seald, chiffrée par le mot de passe, depuis le serveur SSKS
await seald.ssksPassword.retrieveIdentity({
userId: 'myUserId',
password: 'user-known-secret-password'
})
L'instance du SDK est désormais prête à être utilisée !
WARNING
Il est déconseillé de récupérer la même identité avec ssksPassword.retrieveIdentity
sur plusieurs appareils en même temps, au même instant exact, par exemple lors de tests automatisés. Veuillez attendre que l'un des appareils ait fini de récupérer l'identité avant de lancer la récupération sur un autre appareil.
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.
const seald = SealdSDK({ appId, apiURL, plugins: [SealdSDKPluginSSKSPassword(keyStorageURL)] })
await seald.initialize()
// Récupérer l'identité Seald, chiffrée par l'ancien mot de passe, depuis le serveur SSKS
await seald.ssksPassword.retrieveIdentity({
userId: 'myUserId',
password: 'user-known-secret-password' // ancien mot de passe
})
// Changer le mot de passe
await seald.ssksPassword.changeIdentityPassword({
userId: 'myUserId',
currentPassword: 'user-known-secret-password', // ancien mot de passe
newPassword: 'new-user-known-secret-password' // nouveau mot de passe
})
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.
Personnaliser la dérivation de mot de passe
Comme expliqué précédemment, le mot de passe fourni par l'utilisateur est dérivé en une "clé de stockage", et une "clé de chiffrement". Cette dérivation s'effectue avec scrypt
.
Dans le cadre d'une utilisation avancée, vous pouvez néanmoins court-circuiter ce mécanisme, et directement passer une "clé de stockage" et une "clé de chiffrement" brutes en arguments.
La clé de stockage n'a pas besoin d'être secrète par rapport à votre serveur. Il faut cependant absolument qu'elle soit unique, et secrète de tout autre utilisateur, car elle permettrait de supprimer l'identité enregistrée. Elle doit être un string, de 256 caractères au maximum, et composé des caractères autorisés suivants: /A-Za-z0-9+\/=\-_@./
.
Pour celle-ci, plusieurs stratégies sont possibles :
- vous pouvez utiliser un string aléatoire générée par le serveur, et la passer simplement au front-end
- vous pouvez également le dériver du mot de passe, potentiellement avec un autre sel stocké sur le serveur
La clé de chiffrement ne peut être connue que de l'utilisateur dans son frontend et doit absolument rester secrète de tout autre acteur, y compris votre serveur. Le format attendu est un string représentant 64 octets encodés en Base64. La manière usuelle de faire est de la dériver du mot de passe, si possible avec un sel unique aléatoire généré et stocké par votre serveur.
Exemple de dérivation personnalisée de rawEncryptionKey
avec PBKDF2 :
const PBKDF2 = require('pbkdf2')
const util = require('util')
const pbkdf2 = util.promisify(PBKDF2.pbkdf2)
const derivedKeyBuffer = await pbkdf2(userPassword, serverStoredSalt, 1, 64, 'sha512')
const derivedKey = derivedKeyBuffer.toString('base64')
await sdk.ssksPassword.saveIdentity({
userId,
// Le format de `rawEncryptionKey` est strict :
// cela doit être exactement 64 octets, encodés en Base64
rawEncryptionKey: derivedKey,
// `rawStorageKey` est plus souple : elle doit être unique et secrète,
// mais peut être un string arbitraire, jusqu'à 256 caractères de long
// Caractères autorisés : /A-Za-z0-9+\/=\-_@./
rawStorageKey: serverStoredRawStorageKey
})
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 SDK 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) :
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 :
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 (chez OVH 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 lesencryptedIdentity
aux adresses e-mail.
WARNING
Les adresses emails et numéros de téléphones envoyés au serveur SSKS doivent absolument être normalisés.
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ètetwoManRuleKey
au front-end que nous symboliserons parAPI.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.
// On instancie le SDK
const seald = SealdSDK({ appId, apiURL, plugins: [SealdSDKPluginSSKS2MR(keyStorageURL)] })
await seald.initialize()
// On crée une identité Seald comme vu dans la première partie
await seald.initiateIdentity({ signupJWT: 'signup-jwt-value' })
// On fait un appel API au serveur de l'application pour récupérer le `sessionId` et le `twoManRuleKey`
const { sessionId, twoManRuleKey } = APIClient.getSSKSSession() // point d'API à développer de votre côté
await seald.ssks2MR.saveIdentity({
userId: 'myUserId',
sessionId,
// l'adresse e-mail de l'utilisateur doit être répétée pour éviter un MITM par votre sevreur applicatif
authFactor: {
type: 'EM',
value: 'user@domain.com'
},
twoManRuleKey // `twoManRuleKey` est la clé stockée par votre serveur applicatif pour protéger l'identité de cet utilisateur
})
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 :
// Créer une sous-identité
const { identity: mySubIdentity } = await seald.createSubIdentity()
// Sauvegarder la nouvelle identité Seald, chiffrée par `twoManRuleKey`, sur le serveur SSKS
await seald.ssks2MR.saveIdentity({
userId: 'myUserId',
sessionId,
authFactor: {
type: 'EM',
value: 'user@domain.com'
},
twoManRuleKey,
identity: mySubIdentity
})
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 ou SMS, 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.
TIP
Dans un environnement de test, votre serveur peut utiliser l'argument fake_otp: true
lors de la génération de session, afin de ne pas réellement envoyer le challenge
. Celui-ci sera alors fixé à 'aaaaaaaa'
.
Pour plus de détail, voir le paragraphe dédié dans le guide à propos du 2-man-rule.
Il faut ensuite appeler la fonction sdk.ssks2MR.retrieveIdentity
avec les arguments userId
, sessionId
, email
, challenge
et twoManRuleKey
.
// On instancie le SDK
const seald = SealdSDK({ appId, apiURL, plugins: [SealdSDKPluginSSKS2MR(keyStorageURL)] })
await seald.initialize()
// On fait un appel API au serveur de l'application pour récupérer le `sessionId` et la `twoManRuleKey`
const { sessionId, twoManRuleKey } = APIClient.getSSKSSession() // point d'API à développer
/* un email est envoyé à l'utilisateur contenant un `challenge` valable 6h */
/* ... */
/* le `challenge` est récupéré par l'utilisateur et donné dans le contexte de la page */
await seald.ssks2MR.retrieveIdentity({
userId: 'myUserId',
sessionId,
// l'adresse e-mail de l'utilisateur doit être répétée pour éviter un MITM par votre sevreur applicatif
authFactor: {
type: 'EM',
value: 'user@domain.com'
},
challenge: 'challenge envoyé par email',
twoManRuleKey // `twoManRuleKey` est la clé stockée par votre serveur applicatif pour protéger l'identité de cet utilisateur
})
Votre instance du SDK est alors prête à être utilisée !
WARNING
Il est déconseillé de récupérer la même identité avec ssks2MR.retrieveIdentity
sur plusieurs appareils en même temps, au même instant exact, par exemple lors de tests automatisés. Veuillez attendre que l'un des appareils ait fini de récupérer l'identité avant de lancer la récupération sur un autre appareil.
Changement du authFactor
d'un utilisateur
Dans le cycle de vie de l'utilisateur dans votre application, celui-ci peut parfois avoir besoin de changer son adresse email, ou son numéro de téléphone.
Si ce facteur d'authentification est utilisé pour stocker son identité Seald sur SSKS, il va falloir également réenregistrer son identité sur cette nouvelle adresse email ou numéro de téléphone.
Pour ce faire, il n'y a pas de point d'API spécifique dans l'API serveur SSKS, ni de fonction spécifique dans le plugin SSKS du SDK. Il suffit en effet de stocker l'identité sur le nouveau facteur (si l'identité n'est pas présente en local, il faudra préalablement la récupérer sur l'ancien facteur), puis d'utiliser l'API serveur pour supprimer l'identité stockée sur l'ancien facteur.
WARNING
Afin de minimiser le risque de perte de l'identité de l'utilisateur, nous recommandons de d'abord stocker l'identité sur le nouveau authFactor
avant de la supprimer de l'ancien.
Utilisation d'une TwoManRule Key brute
Dans les appels des fonctions ssks2MR.saveIdentity
et ssks2MR.retrieveIdentity
, vous pouvez aussi remplacer twoManRuleKey
par rawTwoManRuleKey
.
Attention, rawTwoManRuleKey
doit absolument être un string représentant l'encodage Base64 d'un buffer cryptographiquement aléatoire de 64 octets.
Techniquement, ceci permet d'éviter que twoManRuleKey
soit dérivé avec scrypt
, ce qui améliore la vitesse d'exécution.
Si vous avez le moindre doute, utilisez plutôt twoManRuleKey
.
Utilisation de rawTwoManRuleKey
:
const seald = SealdSDK({ appId, apiURL, plugins: [SealdSDKPluginSSKS2MR(keyStorageURL)] })
await seald.initialize()
// Utilisation d'une `rawTwoManRuleKey` avec `ssks2MR.saveIdentity`
await sdk.ssks2MR.saveIdentity({
userId,
sessionId,
authFactor,
rawTwoManRuleKey: rawKey
})
// Utilisation d'une `rawTwoManRuleKey` avec `ssks2MR.retrieveIdentity`
await sdk.ssks2MR.retrieveIdentity({
userId,
sessionId,
authFactor,
challenge,
rawTwoManRuleKey: rawKey
})
Génération d'une rawTwoManRuleKey
adéquate :
const crypto = require('crypto')
const util = require('util')
const randomBytes = util.promisify(crypto.randomBytes)
const rawKeyBuffer = await randomBytes(64)
// Ce format est strict : cela doit être exactement 64 octets, encodés en Base64
// et provenir d'une source d'aléa cryptographiquement sûre
const rawKey = rawKeyBuffer.toString('base64')
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
contenant l'identité courante de l'instance SDK.
Réciproquement, sdk.importIdentity
importe un Buffer
d'identité dans l'instance SDK afin de la rendre fonctionnelle.
DANGER
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.