Le processus d'authentification du visiteur consiste en la création d'un Token (ou jeton) signé et crypté qui est créé depuis votre backend et transmis au tag iAdvize via votre frontend (site web) afin qu'iAdvize puisse reconnaître le visiteur et afficher les données relatives au visiteur et à la conversation en conséquence.
1. Informations importantes et recommandations
1.1 Identifiant de l'utilisateur
⚠ Il doit être unique par utilisateur - l'ID utilisateur ne peut pas être recyclé d'un utilisateur à l'autre.
⚠ Il doit comporter 255 caractères maximum.
⚠ Si vous ne respectez pas ces directives, iAdvize considérera tous les visiteurs comme un seul et même visiteur. Nous associerons alors toutes les conversations des visiteurs au même identifiant utilisateur. Cela crée un problème de confidentialité : les visiteurs auront alors accès au contenu des conversations de chacun, y compris le texte et les pièces jointes.
1.2 Cryptage des Tokens (ou jetons)
Lorsque vous générez un JWE qui contient votre identifiant utilisateur, votre bibliothèque pour générer ce jeton doit prendre en charge A256GCM et RSA_OAEP_256 pour créer le JWE. Le JWS interne doit être signé avec RS256.
1.3 Stockage des clés privées
Nous stockons notre clé privée à l'aide d'un outil de sécurité externe, de sorte que notre clé privée n'est pas exposée à travers notre code ou tout accès à la base de données.
1.4 À propos de l'usage de l'identifiant Externe (extID)
Le système d'authentification des visiteurs remplace entièrement l'utilisation de l'"ExtID". Ainsi, si vous utilisez le système d'authentification des visiteurs dans un espace authentifié de votre site web, vous devez vous assurer que vous n'utilisez pas le système "ExtID" en parallèle.
1.5 Envoi de données sur les visiteurs dans le jeton JWT
En plus de la déclaration de l'ID de l'utilisateur, une déclaration facultative de données du visiteur peut être ajoutée au jeton JWT. Voici à quoi il ressemblerait, avant le cryptage :
Encoded | Decoded |
eyJhbGciOiJSUzI1NiJ9.eyJodHRwczpcL1wvaWFkdml6ZS5jb21cL |
{ "https://iadvize.com/userId": "test_documentation", "iss": "https://test.iadvize.com", "https://iadvize.com/visitorData": { "country": "France", "firstName": "Jane", "lastName": "Doe", "zipCode": "44000", "address": "9 rue Nina Simone", "phoneNumber": "+33651229856", "city": "Nantes", "email": "jane.doe@email.com" }, "exp": 1690376935 } |
Detail here |
Il peut contenir les champs suivants, tous sont des chaînes de caractères optionnelles :
address
city
country
firstName
lastName
phoneNumber
zipCode
Ces champs seront ensuite affichés sur le pupitre du conseiller :
Voir la section 3. pour la mise en œuvre du backend technique.
2. Processus détaillé de signature et de chiffrement
2.1 Génération et cryptage de paires de clés
Vous devrez générer une paire de clés : une clé publique/ une clé privée. Vous devrez partager votre clé publique avec iAdvize.
iAdvize vous fournit sa clé publique.
Une façon de générer vos clés (vous pouvez adapter le nombre de bits) :
openssl genpkey -out rsaPrivateKey.pem -algorithm RSA -pkeyopt rsa_keygen_bits:2048
openssl rsa -in rsaPrivateKey.pem -pubout -out rsaPublicKey.pem
2.2 Signature et chiffrement
iAdvize effectuera le processus inverse qui consiste à décrypter le JWE (à l'aide de la clé privée d'iAdvize) et à vérifier la signature du token (à l'aide de la clé publique du Client) pour finalement extraire l'identifiant de l'utilisateur et créer une Session Authentifiée Visiteur (un nouveau JWS généré par iAdvize en interne) :
2.4 Clé publique d'iAdvize (à utiliser pour la production)
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1KdAzuUa5rOXgLHavoDRYoNXzwWz/p
FhgGypYFbvV8DNjB93XK2AzKTwW+vxT7RYl4f+sKLdEi3dJYgPt2hquhTNmFxAzRvTuolUOKr1XN
x7QbDj+7cfLVDYjmds/ydNtyHi8TUHSvfzs8SGXO5E5H13llmayPEslHKShG0cLIDcLNr6hJcfv9fvO
ZqQlLQ4Bx7to/66IHke9zY+1oidrUdFGzxXG+RGK81mIMuXj6N2EGJ7YYcQqXJfBJnWFlSGCQNtt
w5Rfj00eZbkMRO3XohhNqGIiBG2tejSjfB53UpiHdbzni+tyB72R5aaq4d+gkkgaOVYn/Or2fArOH2
FUQIDAQAB
3. Implémentation technique Backend
À noter : vous devrez définir une "claim" personnalisée qui sera intégrée dans le jeton signé (JWS). Vous devez préfixer toutes les "claims" avec https://iadvize.com/.
⚠️ La claim https://iadvize.com/visitorData est facultative ⚠️
3.1 La librairie JWT
Tout d'abord, pour utiliser JWT, vous devez choisir la bonne bibliothèque qui correspond à la langue de votre backend.
Vous pouvez trouver sur le site officiel de JWT une liste de nombreuses bibliothèques implémentées pour de nombreux langages.
La bibliothèque choisie doit supporter "A256GCM" et "RSA_OAEP_256" pour créer le JWE, le JWS interne doit être signé avec "RS256".
3.2 Un exemple en SCALA
Vous avez ici une mise en œuvre technique de la solution en SCALA :
import java.security.interfaces.RSAPublicKey import java.security.spec.{PKCS8EncodedKeySpec, X509EncodedKeySpec} import java.security.{KeyFactory, KeyPairGenerator, PrivateKey, PublicKey} import java.util.Date import com.nimbusds.jose._ import com.nimbusds.jose.crypto._ import com.nimbusds.jwt.{JWTClaimsSet, SignedJWT} object JWEBuilder { def main(args: Array[String]): Unit = { val (clientPubKey, clientPrivateKey) = getClientKeys() val (iadvizePubKey, iadvizePrivateKey) = getIAdvizeKeys() val JWS1 = createJWS(clientPrivateKey) val JWE1 = createJWE(iadvizePubKey, JWS1) println(s"JWS : ${JWS1.serialize()}") println(s"JWE : ${JWE1.serialize()}") val token = JWE1.serialize() val JWS2 = decryptJWE(iadvizePrivateKey, token) println(s"Is valid JWS : ${JWS2.verify(new RSASSAVerifier(clientPubKey.asInstanceOf[RSAPublicKey]))}") println(s"${JWS2.getJWTClaimsSet}") } def getClientKeys() : (PublicKey, PrivateKey) = { val generator = KeyPairGenerator.getInstance("RSA") generator.initialize(2048) val pairClient = generator.generateKeyPair val pubKeyClient = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(pairClient.getPublic.getEncoded)) val privateKeyClient = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(pairClient.getPrivate.getEncoded)) (pubKeyClient, privateKeyClient) } def getIAdvizeKeys() : (PublicKey, PrivateKey) = { val generator = KeyPairGenerator.getInstance("RSA") generator.initialize(2048) val pairIadvize = generator.generateKeyPair val pubKeyIadvize = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(pairIadvize.getPublic.getEncoded)) val privateKeyIadvize = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(pairIadvize.getPrivate.getEncoded)) (pubKeyIadvize, privateKeyIadvize) } def createJWS(clientPrivateKey: PrivateKey) : SignedJWT = { val claimsSet = new JWTClaimsSet.Builder() // You can define custom claims. Here you can define your User ID which will be embed in the signed token(JWS). // You have to prefix all the claims with `https://iadvize.com/”. // The claim https://iadvize.com/userId is mandatory. claimsSet.claim("https://iadvize.com/userId","c42ab96d-0637-4d1e-8be3-0a872d9d1ef1") // For security reason it’s better to set a quick expiration time. As this token will just be used to initialise a new secured visitor session on iAdvize 1 minute seems a good duration. claimsSet.expirationTime(ZonedDateTime.now().plusMinutes(1)) val signer = new RSASSASigner(clientPrivateKey) val signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.RS256), claimsSet.build()) signedJWT.sign(signer) signedJWT } def createJWE(iadvizePublicKey : PublicKey, jws : SignedJWT) : JWEObject = { val header = new JWEHeader.Builder(JWEAlgorithm.RSA_OAEP_256, EncryptionMethod.A256GCM).build() val payload = new Payload(jws) val jwe = new JWEObject(header, payload) jwe.encrypt(new com.nimbusds.jose.crypto.RSAEncrypter(iadvizePublicKey.asInstanceOf[java.security.interfaces.RSAPublicKey])) jwe } def decryptJWE(iadvizePrivateKey: PrivateKey, token : String): SignedJWT = { val o = JWEObject.parse(token) o.decrypt(new RSADecrypter(iadvizePrivateKey)) o.getPayload.toSignedJWT } }
Les éléments clés pour vous sont les fonctions "createJWS()" et "createJWE()" que nous allons détailler ci-dessous.
"createJWS()" :
def createJWS(yourPrivateKey: PrivateKey) : SignedJWT = { val claimsSet = new JWTClaimsSet.Builder() claimsSet.claim("https://iadvize.com/userId","c42ab96d-0637-4d1e-8be3-0a872d9d1ef1") // For security reason, it’s better to set a quick expiration time. As this token will just be used to initialise a new secured visitor session on iAdvize 1 minute seems a good duration. claimsSet.expirationTime(ZonedDateTime.now().plusMinutes(1)) val signer = new RSASSASigner(clientPrivateKey) val signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.RS256), claimsSet.build()) // Sign the JWT using your private key: it became a JWS. signedJWT.sign(signer) signedJWT }
Pour des raisons de sécurité, il est préférable de fixer un délai d'expiration rapide. Comme ce jeton ne sera utilisé que pour initialiser une nouvelle session sécurisée de visiteur sur iAdvize, 1 minute semble une bonne durée.
createJWE() :
Une fois que nous avons un JWT signé, un JWS, nous pouvons crypter ce JWS pour finalement avoir un JWE.
def createJWE(iadvizePublicKey : PublicKey, jws : SignedJWT) : JWEObject = { // Specify the header(encryption algorithms) and the payload (the JWS generated in the previous step). val header = new JWEHeader.Builder(JWEAlgorithm.RSA_OAEP_256, EncryptionMethod.A128CBC_HS256).build() val payload = new Payload(jws) val jwe = new JWEObject(header, payload) // Encrypt the token using the iAdvize Public key. jwe.encrypt(new com.nimbusds.jose.crypto.RSAEncrypter(iadvizePublicKey.asInstanceOf[java.security.interfaces.RSAPublicKey])) jwe }
Voici comment la fonction createJWS serait modifiée pour ajouter les champs firstName et lastName :
def createJWS(clientPrivateKey: PrivateKey) : SignedJWT = {
val claimsSet = new JWTClaimsSet.Builder()
claimsSet.claim("https://iadvize.com/userId","c42ab96d-0637-4d1e-8be3-0a872d9d1ef1")
claimsSet.claim("https://iadvize.com/visitorData", JSONObjectUtils.parse("""{"firstName: "Jane", "lastName": "Doe"}"""))
// For security reason, it’s better to set a quick expiration time. As this token will just be used to initialise a new secured visitor
session on iAdvize 1 minute seems a good duration.
claimsSet.expirationTime(ZonedDateTime.now().plusMinutes(1))
val signer = new RSASSASigner(clientPrivateKey)
val signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.RS256), claimsSet.build())
//Sign the JWT using your private key: it became a JWS.
signedJWT.sign(signer)
signedJWT
}
Dans l'exemple ci-dessus, la réclamation visitorData est un objet JSON contenant les champs "firstName" et "lastName". Voici un exemple complet avec tous les champs possibles :
{ "country": "France", "firstName": "Jane", "lastName": "Doe", "zipCode": "44000", "address": "9 rue Nina Simone", "phoneNumber": "+33651229856", "city": "Nantes", "email": "jane.doe@email.com" }
Voici à quoi ressemblerait la même fonction en Node.js :
const jwt = require("jsonwebtoken"); function createJWS(clientPrivateKey) { return jwt.sign( { "https://iadvize.com/userId": "test_documentation", iss: "https://test.iadvize.com", "https://iadvize.com/visitorData": { firstName: "Jane", lastName: "Doe", }, // For security reasons, it’s better to set a small expiration time. As this token will just be used to initialise a new secured visitor session on iAdvize, 1 minute seems like a good duration. exp: Date.now() + 60 * 1000, }, clientPrivateKey ); }
4. FAQ
4.1. Où puis-je trouver la clé publique d'iAdvize ?
4.2. Mon visiteur est authentifié (un JWT est dans le stockage local) mais je n'ai pas de cadenas 🔒 sur le bureau de l'agent.
- Assurez-vous, lorsque vous êtes dans un espace authentifié de votre site web où l'authentification des visiteurs est activée, de supprimer l'utilisation du système "extID" : À propos de l'utilisation de l'identifiant externe (extID)
4.3. JWT n'est pas valide
- Assurez-vous de définir toutes les allégations requises avec les bons préfixes.
{ "https://iadvize.com/userId":"myuserid", "iss":"https://livechat.iadvize.com", "exp":1602060589 }
- Assurez-vous que le JWT est signé avec le bon algorithme.
{ "alg": "RS256" }
- Assurez-vous que le JWE est crypté avec le bon algorithme.
{ "enc": "A256GCM", "alg": "RSA-OAEP-256" }
- Assurez-vous que vous utilisez la bonne clé privée et la bonne clé publique d'iAdvize. Assurez-vous qu'iAdvize a configuré votre clé publique dans vos paramètres.