Skip to main content
Soft identification, sometimes called “soft login” or “soft ID”, allows identification of a contact who has arrived at a webpage via an email link. When your Engage environment is configured for soft identification, an encrypted JSON is sent in the link’s query string, which has the following form:
eclub=q6KCtl2WUiWxd5UzDMcfKNuZonZ6bVx7LZm2y8ctNJVJm4Oiyz339wWMIuVAzHIwikEdtDFYllMLwMhMZ4GTn55SBGjSn2ZQsDtCEDJ-zG7ptW6hC9n2UO2NVxaijERvURtBPFIE35EZPPSk_PAZHmQp72C0N6-h1xgfok9lb-9IV_hWorlegR8sD64p3SOy
In this data is contained all the information needed to identify the contact and present them with a personalized web experience. When decrypted, the JSON has the following structure:
{
"contactId": "1c9b35d6-28d8-4520-8900-a89555f7f0c5",   
"email": "example@example.com",
"dateTime": "2018-07-11T11:19:54Z",
"discoveryKey" : "6iub35d6-34d8-6666-8900-e89100f7f0c5"
}
  • contactId: The unique ID for the contact in Engage, used for subsequent API-calls to get all relevant information about a contact
  • email: The contact’s email address
  • id: The contact’s identification key as defined in Engage (for example, “email”)
  • dateTime: The time and date the link was generated (V.5)
  • discoveryKey: The contact’s identifier for Voyado Elevate (optional)
To include the discoveryKey from Voyado Elevate, ask your Voyado team to set it up for you.
Be careful about the access a user is granted via a soft login. A soft login link is a convenient method of identification but it can also be easily shared in error, for example by a user forwarding an email containing the link to a friend.

Decoding the message

1

Extracting the value

Extract the value string from the parameter “eClub”
2

Decoding the string

Decoding the string from URL-safe Base64”:
  • Replace all “-” with “+”
  • Replace all “_” with “/”
  • Add padding characters, based on the length
  • Decode the string using normal Base64 decoding
3

Extracting the data

The initialization vector is stored in the first 16 bytes; the remainder is the encoded message”
4

Decrypting the message

With the shared key and initialization vector from step 3, decrypt the message using AES-256 (CBC)

Encoding the message

1

Data format

The data is sent as a JSON with the structure shown in the example above.
2

Encrypting data

This JSON string is then encrypted using AES-256 and the shared key which both the customer and Voyado have (it was exchanged during implementation) and a unique initialization vector for each message.
3

Preparing data

The initialization vector and the encrypted string are concatenated, Base64 encoded and modified for URL inclusion (URL-safe Base64).
4

Sending data

The complete encoded string is then sent as the query-string parameter “eClub”.
The shared key used is 32 characters long, using no special characters, only a-z, A-Z, 0-9.
An example of such a key is: DaqfT7Ys6tGqM3zbesEKpmacTJEqBCp2. There are online tools available for creating keys, such as Strong Random Password Generator.
public static string Decrypt(string keyString, string stringToDecrypt)
{
    var key = Encoding.UTF8.GetBytes(keyString);
    stringToDecrypt = stringToDecrypt.Replace('-', '+').Replace('_', '/');
    switch (stringToDecrypt.Length % 4)
    {
        case 2: stringToDecrypt += "=="; break;
        case 3: stringToDecrypt += "="; break;
    }
    var bytes = Convert.FromBase64String(stringToDecrypt);
    var iv = new byte[16];
    var text = new byte[bytes.Length - 16];
    Array.Copy(bytes, iv, 16);
    Array.Copy(bytes, 16, text, 0, text.Length);
    string plaintext = null;
    using (var aes = Aes.Create())
    {
        aes.Padding = PaddingMode.PKCS7;
        aes.IV = iv;
        aes.Key = key;
        aes.Mode = CipherMode.CBC;
        var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
        using (var msDecrypt = new MemoryStream(text))
        {
            using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
            {
                using (var srDecrypt = new StreamReader(csDecrypt))
                {
                    plaintext = srDecrypt.ReadToEnd();
                }
            }
        }
    }

    return plaintext;
}
use Crypt::Mode::CBC;
use MIME::Base64::URLSafe;

my $key = 'THE SHARED KEY';
my $input = 'THE ENCRYPTED VALUE';

$input = urlsafe_b64decode($input);
my $iv = substr($input, 0, 16);
my $data = substr($input, 16);
my $cbc = Crypt::Mode::CBC->new('AES');
my $decrypted = $cbc->decrypt($data, $key, $iv);
function decrypt($key, $encrypted)
{
    $encrypted = base64_decode(strtr( $encrypted, '-_', '+/') . str_repeat('=', 3 - ( 3 + strlen( $encrypted )) % 4 ));
    $iv = substr($encrypted, 0, 16);
    $encrypted = substr($encrypted, 16);
    
    $decrypted = openssl_decrypt($encrypted,'aes-256-cbc',$key,OPENSSL_RAW_DATA, $iv);
                
    return $decrypted;
}
const crypto = require('crypto');

const stringToDecrypt = "INSERT STRING TO DECRYPT HERE";
const keyString = "INSERT KEY HERE";

const key = Buffer.from(keyString, 'utf8');
let stringToDecryptFixed = stringToDecrypt.replace('-', '+').replace('_', '/');
switch (stringToDecryptFixed.length % 4) {
    case 2: stringToDecryptFixed += "=="; break;
    case 3: stringToDecryptFixed += "="; break;
}
const bytes = Buffer.from(stringToDecryptFixed, 'base64');
const iv = Buffer.alloc(16);
const text = bytes.slice(16);
bytes.copy(iv, 0, 0, 16);

let plaintext = null;
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
decipher.setAutoPadding(false);
plaintext = Buffer.concat([decipher.update(text), decipher.final()]).toString('utf-8');

console.log(plaintext);