# API de SSKS

L'URL par défaut de SSKS est ssks.seald.io pour l'environnement de production, et ssks.staging-0.seald.io pour l'environnement de tests.

# En-têtes des requêtes

L'authentification se fait par des headers HTTP :

Variable Type Description
X-SEALD-APPID (requis) String Application ID, donné sur le tableau d'administration du Seald SDK
X-SEALD-APIKEY (requis) String Clé d'API, donnée sur le tableau d'administration du Seald SDK
Content-type String application/json

Pour savoir où trouver ces valeurs, référez-vous au paragraphe en question dans le guide concernant l'intégration du 2-man-rule.

# Requêtes pour la protection par 2-man-rule

# Envoi d'un challenge

Ce point d'API est appelé par le serveur de l'application utilisant le SDK, et permet de :

  • Créer un "utilisateur" au sens de SSKS (si create_user est à true) ;
  • Créer une "Session" ;
  • Si une identité a déjà été stockée sur SSKS avec ce auth_factor (adresse email ou numéro de téléphone, voir le paragraphe Vérification du must_authenticate pour les détails de la comparaison), ou si force_auth est mis à true, alors must_authenticate est mis à true ; si non, à false ;
  • Retourner un session_id et must_authenticate ;
  • Si must_authenticate est true, envoyer un challenge à l'utilisateur, par email ou SMS, le backend ne doit pas avoir accès à ce challenge. Le challenge est valable 6h.

Le backend doit renvoyer le session_id et must_authenticate au frontend, tel qu'expliqué dans le guide d'intégration du 2-man-rule sur le backend.

TIP

L'envoi de challenge par SMS est limité au quota de SMS autorisé pour votre team. Si le quota est dépassé, votre requête recevra une erreur 406 Not Acceptable: {"detail": "SMSQuotaFailed"}.

N'hésitez pas à nous contacter pour augmenter le quota de SMS de votre équipe.

# URL

POST https://{SSKS_URL}/tmr/back/challenge_send/

# Format du corps de la requête

Il s'agit d'une requête POST au format JSON :

Variable Type Description
create_user (default: false) Boolean Si true, créé un utilisateur s'il n'existe pas. Sinon rejette avec une erreur 404. Si inutilisé, l'application doit manuellement créer un utilisateur
user_id (requis) String Identifiant unique de l'utilisateur. Peut être un ID, un UUID, une adresse email. La donnée est stockée en clair sur SSKS.
auth_factor (requis) Object Facteur authentication de l'utilisateur. Utilisée pour envoyer le challenge. Peut être identique pour plusieurs utilisateurs tant que le user_id est unique.
auth_factor.type (requis) String Type de facteur d'authentification. "EM" pour une adresse email, "SMS" pour un numéro de téléphone.
auth_factor.value (requis) String Adresse e-mail ou numéro de téléphone de l'utilisateur. Utilisé pour envoyer le challenge. Doit absolument être normalisé. La valeur n'est pas stockée en clair sur SSKS, consultez le paragraphe Stockage des facteurs d'authentification pour plus de détails.
email (deprecated) String Obsolète, utilisez auth_factor. Adresse e-mail de l'utilisateur.
force_auth (default: false) Boolean Permet de forcer l'envoi d'un challenge d'authentification, même si ce auth_factor n'a jamais été utilisé précédemment.
template (requis) String Template au format HTML de l'email contenant le challenge d'authentification envoyé à l'utilisateur. Doit contenir le String $$CHALLENGE$$ qui sera remplacé par le challenge par SSKS.
subject String Objet de l'email envoyé avec le challenge. Défaut à "End-to-end encryption challenge". Ignoré pour le SMS.
fake_otp (default: false) Boolean Uniquement en environnement de test / staging. Si true, n'enverra pas d'email ou SMS. Le challenge sera toujours aaaaaaaa. Si true en environnement de production, renverra une erreur 406.

# Format de la réponse

Au format JSON :

Variable Type Description
session_id String ID de la session ainsi créée. Doit être transmis au frontend.
must_authenticate Boolean Booléen indiquant si la session créée a besoin d'un challenge. Si true, le serveur envoie un email à l'utilisateur, contenant ce challenge, qu'il devra répéter dans le frontend dans les 6h. Si false, la session peut directement enregistrer son identité sur SSKS sans challenge.

# Vérification du must_authenticate

Ce point d'API peut être utilisé par le serveur de l'application utilisant le SDK avant d'envoyer le challenge pour vérifier la valeur que prendrait must_authenticate si le point d'api d'envoi était appelé.

Cela permet de déterminer si une identité a déjà été stockée pour un auth_factor donné.

TIP

Pour décider de la valeur de must_authenticate, le serveur SSKS n'utilise pas simplement le facteur d'authentification brut donné par la requête pour vérifier si une identité a déjà été stocké avec celui-ci ou pas. Il essaye aussi d'enlever les alias potentiellement présents.

Par exemple, pour les adresses emails Gmail, ce qui suit le + est ignoré : email@gmail.com et email+alias@gmail.com sont considérés comme équivalents.

La procédure précise utilisée pour ce faire est susceptible d'être modifiée pour être améliorée.

# URL

POST https://{SSKS_URL}/tmr/back/must_authenticate/

# Format du corps de la requête

Il s'agit d'une requête POST au format JSON :

Variable Type Description
type (requis) String "EM" pour une adresse e-mail, "SMS"" pour un numéro de téléphone.
value (requis) String Adresse e-mail ou numéro de téléphone de l'utilisateur. Doit absolument être normalisé.

# Format de la réponse

Au format JSON :

Variable Type Description
must_authenticate Boolean Booléen indiquant si la session créée a besoin d'un challenge. Si true, le serveur devra envoyer un email à l'utilisateur au prochain send_challenge.

# Création manuelle d'un utilisateur

Ce point d'API peut être utilisé si create_user est à False sur le point d'API précédent. Cela va juste créer un "utilisateur" vierge au sens de SSKS.

# URL

POST https://{SSKS_URL}/tmr/back/create_user/

# Format du corps de la requête

Variable Type Description
user_id (requis) String Identifiant unique de l'utilisateur. Peut être un ID, un UUID, une adresse email. La donnée est stockée en clair sur SSKS.
auth_factor (requis) Object facteur authentication de l'utilisateur. Utilisée pour envoyer le challenge.
auth_factor.type (requis) String Type de facteur d'authentification. 'EM' pour une adresse email, 'SMS' pour un numéro de téléphone.
auth_factor.value (requis) String Adresse e-mail ou numéro de téléphone de l'utilisateur. Doit absolument être normalisé. La valeur n'est pas stockée en clair sur SSKS, consultez le paragraphe Stockage des facteurs d'authentification pour plus de détails.
email (deprecated) String Obsolète, utilisez auth_factor. Adresse e-mail de l'utilisateur.

# Réponse

Ce point d'API n'a pas d'information à retourner au serveur. Il répond simplement avec {"status": "ok"}.

# Suppression d'un utilisateur

Ce point d'API peut être utilisé pour supprimer toutes les données stockées sur un utilisateur sur SSKS.

# URL

POST https://{SSKS_URL}/tmr/back/delete_user/

# Format du corps de la requête

Variable Type Description
user_id (requis) String Identifiant unique qui avait été utilisé pour créer l'utilisateur à supprimer.
auth_factor (optionnel) Object Objet contenant les informations d'authentification d'un utilisateur. Si renseigné, uniquement l'utilisateur possédant ce auth_factor sera supprimé. (Cela peut être utile en cas de doublons d'user_id)
auth_factor.type (requis si auth_factor est utilisé) String Type de facteur d'authentification. 'EM' pour une adresse email, 'SMS' pour un numéro de téléphone.
auth_factor.value (requis si auth_factor est utilisé) String Adresse e-mail ou numéro de téléphone de l'utilisateur. Doit absolument être normalisé.

# Réponse

Variable Type Description
status String ok si la requête s'est déroulée correctement
deleted Number Nombre d'identité supprimées

# Vérification de l'existence d'une identité

Ce point d'API peut être utilisé pour vérifier si pour un user_id (éventuellement couplé avec un auth_factor) donné une identité protégée en 2-man-rule existe.

Il répond avec le nombre d'identités correspondant à cet utilisateur, soit 0 quand il n'y en a pas ou si l'utilisateur n'existe pas, soit un nombre strictement positif s'il y en a une ou plus.

Il est aussi possible de vérifier s'il y a une identité stockée pour un user_id sans vérifier le auth_factor auquel il est associé.

# URL

POST https://{SSKS_URL}/tmr/back/identity_check/

# Format du corps de la requête

Variable Type Description
user_id (requis) String Identifiant unique qui a été utilisé pour créer l'utilisateur à vérifier.
auth_factor (optionnel) Object Facteur authentication de l'utilisateur. Utilisée pour envoyer le challenge.
auth_factor.type (requis si auth_factor.value est donné) String Type de facteur d'authentification. 'EM' pour une adresse email, 'SMS' pour un numéro de téléphone.
auth_factor.value (requis si auth_factor.type est donné) String Adresse e-mail ou numéro de téléphone de l'utilisateur. Doit absolument être normalisé.

# Réponse

Variable Type Description
identities_count Number Nombre d'identités stockées pour ce user_id éventuellement filtré par auth_factor.
user Object Informations sur l'utilisateur requêté
user.user_id Object user_id donné dans la requête
user.app_id Object app_id de l'application dans Seald

# Normalisation des authFactors

Lors de l'utilisation de SSKS pour la protection en 2-man-rule, tous les authFactors envoyés à SSKS doivent être normalisés. Si un authFactor est envoyé de façon non-normalisée, le serveur SSKS pourra renvoyer une erreur.

WARNING

Pour l'instant, les authFactors non-normalisés sont encore acceptés, et sont normalisés en interne par le serveur.

Néanmoins, ils sont fortement déconseillés, et seront refusés d'ici quelques mois.

# Normalisation des emails

Les adresses email sont normalisées en :

Pour effectuer cette normalisation, vous pouvez utiliser les snippets suivants :

  • en Python :
import unicodedata
normalized_email = unicodedata.normalize("NFKC", email).replace(" ", "").lower()
  • en JavaScript :
const normalizedEmail = email.normalize('NFKC').replace(/ /g, '').toLowerCase()

# Normalisation des numéros de téléphone

Pour les numéros de téléphone, ceux-ci doivent absolument être envoyés au format standard international E.164 (opens new window).

Celui-ci correspond simplement au numéro de téléphone, sans aucun espace, précédé du code de pays noté avec un +.

Par exemple, les notations suivantes ne sont PAS valides :

  • 01 23 45 67 89
  • 0123456789
  • +33 1 23 45 67 89
  • +33-123456789
  • 0033123456789

Utilisez la notation suivante :

+33123456789

Pour convertir un numéro de téléphone quelconque au format E.164, des librairies existent dans différents langages. Par exemple, pour convertir un numéro en E.164 en considérant que nous sommes en France :

  • en Python, vous pouvez utiliser phonenumbers :
import phonenumbers
phonenumbers.format_number(phonenumbers.parse("01 23 45 67 89", "FR"), phonenumbers.PhoneNumberFormat.E164)
# '+33123456789'
  • en JavaScript, vous pouvez utiliser google-libphonenumber :
import libPhoneNumber from 'google-libphonenumber'
const instance = libPhoneNumber.PhoneNumberUtil.getInstance()
instance.format(instance.parse('01 23 45 67 89', 'FR'), libPhoneNumber.PhoneNumberFormat.E164)
// '+33123456789'

# Stockage des authFactors

La valeur des facteurs d'authentification n'est pas stockée en clair sur SSKS. Elle est d'abord normalisée, puis hachée. Le hash est stocké de manière à ce qu'il soit possible de vérifier que la valeur donnée est la même lorsqu'elle est utilisée ultérieurement.

De plus, une version désaliassée est calculée, puis hachée et stockée, afin que le SSKS puisse décider de la valeur de must_authenticate pour les demandes ultérieures.

Enfin, le auth_factor est également chiffré pour une clé que le serveur ne possède pas, et cette version chiffrée est stockée, afin que Seald soit en mesure, si nécessaire dans le futur, de migrer les procédures de hachage.

# Migration

Au 31/01/2022, la fonction de hash a été mise à jour de la v1 à la v2 pour améliorer les performances en réduisant le nombre d'itérations de hash.

Au 01/01/2023, la fonction de hash a été mise à jour de la v2 à la v3 en introduisant une normalisation préalable, ainsi qu'un mécanisme de désaliassage afin de s'assurer qu'un changement de casse ou d'encodage du authFactor ne soit pas considéré comme un authFactor différent, et que l'utilisation d'alias courants d'emails ne permette pas de contourner une authentification.

Étant donné que la valeur des authFactor pour lesquels une identité chiffrée a été stockée en v1 ou en v2 n'est pas accessible par nos équipes, il nous est impossible de calculer le hash v3 pour ceux-ci. De ce fait, un mécanisme de migration lors de l'utilisation a été mis en œuvre :

Lors d'une requête contenant un authFactor (POST /tmr/back/challenge_send/, POST /tmr/back/must_authenticate/, POST /tmr/back/delete_user/, POST /tmr/back/identity_check/), SSKS calcule le hash v3, le hash v2 et le hash v1, puis :

  1. si le authFactor n'a pas de correspondance sur le hash v3, mais en a une sur le hash v1 ou v2, le authFactor est migré à un stockage en v3 et le vieux hash est supprimé.
  2. si le authFactor a une correspondance sur le hash v3 et a une correspondance sur le hash v1 ou v2, une collision liée à la normalisation est survenue qui notifie les équipes Seald et répond au client une erreur 500. Dans ce cas, il faut gérer la collision manuellement, veuillez nous contacter sur support@seald.io.
  3. sinon, le authFactor a une correspondance sur le hash v3, et pas sur le hash v1 et v2, ce qui est le fonctionnement normal.

DANGER

Il est crucial d'effectuer la migration avec la valeur brute, non-normalisée, des authFactor, telle qu'elle a été donnée lors du stockage de l'identité de l'utilisateur. En effet, dans le cas contraire, le serveur ne pourrait pas trouver la correspondance avec l'ancien hash v1 ou hash v2.

Une fois la migration de votre app terminée, il est recommandé de ne plus envoyé que la version normalisée des authFactor.

TIP

Une fois l'ensemble des utilisateurs d'une application migrés à une version de hash (v2 ou v3), les hashs de version antérieure ne sont plus calculés. En particulier, l'amélioration de performance du liée à l'introduction du hash v2 n'est effective qu'à partir du moment où l'ensemble des utilisateurs sont migrés au hash v2 au moins.

# Effectuer la migration

Pour exécuter la migration sur l'ensemble des utilisateurs, vous devez :

  • Pour chaque utilisateur de votre application :
    • Pour chaque authFactor de cet utilisateur :
      • Faire un appel authentifié à POST /tmr/back/must_authenticate/ avec les paramètres { "type": <"EM"|"SMS">, "value": <value> }. Attention: il faut envoyer la valeur brute, non-normalisée, du authFactor telle qu'elle a été donnée lors du stockage de l'identité de l'utilisateur pour que la migration fonctionne.

Une fois effectuée, l'ensemble des utilisateurs connus dans votre application ont été migrés.

Il se peut que vous ayez d'autres identités inutilisées dans votre application qu'il faudrait supprimer. Pour confirmer que votre migration est bien terminée, veuillez nous contacter sur support@seald.io.

Une fois que vous avez confirmé auprès de Seald que l'ensemble des utilisateurs ont été migrés, vous pouvez normaliser les authFactor enregistrés dans votre application.

# Requêtes pour la protection par mot de passe

# Vérification de l'existence d'une identité

Ce point d'API peut être utilisé pour vérifier si un user_id donné a une ou plusieurs identités qui sont stockées protégées par mot de passe.

Il répond avec le nombre d'identités correspondant à cet utilisateur, soit 0 quand il n'y en a pas, soit un nombre strictement positif s'il y en a.

# URL

POST https://{SSKS_URL}/strict/back/identity_check/

# Format du corps de la requête

Variable Type Description
user_id (requis) String Identifiant unique qui a été utilisé pour créer l'utilisateur à vérifier.

# Réponse

Variable Type Description
identities_count Number Nombre d'identités stockées pour ce user_id.
user Object Informations sur l'utilisateur requêté
user.user_id Object user_id donné dans la requête
user.app_id Object app_id de l'application dans Seald

# Suppression d'identité

Ce point d'API peut être utilisé pour supprimer toutes les identités stockées pour le compte d'un user_id.

Il n'est pas possible de distinguer plusieurs identités d'un même utilisateur. On ne peut donc que les supprimer conjointement.

# URL

POST https://{SSKS_URL}/strict/back/identity_delete/

# Format du corps de la requête

Variable Type Description
user_id (requis) String Identifiant unique qui avait été utilisé pour créer l'utilisateur à supprimer.

# Réponse

Ce point d'API n'a pas d'information à retourner au serveur. Il répond simplement avec {"status": "ok"}.