Note: You will have to define a custom claim which will be embedded in the signed token (JWS). You have to prefix all the claims with https://iadvize.com/
⚠️ The claim https://iadvize.com/visitorData is optional ⚠️
JWT Library
First, to deal with JWT, you will have to choose the right library which fits to your backend language.
You can find on the official JWT website a list of many libraries implemented for many languages.
The chosen library should support "A256GCM and RSA_OAEP_256" for creating the JWE, the inner JWS must be signed with "RS256".
An example in SCALA
Here you have a technical implementation of the solution in 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
}
}
The key parts for you are the functions createJWS() and createJWE() which we will detail below.
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
}
For security reason, it’s better to set a quick expiration time. As this token will just be used to initialize a new secured visitor session on iAdvize 1 minute seems a good duration. createJWE() Once we have a signed JWT, a JWS, we could encrypt this JWS to finally have a 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
}
Here is how the createJWS function would be modified to add the firstName and the lastName fields:
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
}
In the example above, the visitorData claim is a JSON object containing the “firstName” and “lastName” fields. Here is a complete example with all possible fields:
Here is how the same function would look in 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
);
}