Skip to main content

Soft identification

Soft identification, sometimes called "soft login", allows identification of a contact, for example over the web or by an e-com, who has arrived via an email link. When you are configured for soft identification, an encrypted JSON is sent in the link's query string, containing the information needed to identify the contact.

This is what the data can look like in the query string:

eclub=q6KCtl2WUiWxd5UzDMcfKNuZonZ6bVx7LZm2y8ctNJVJm4Oiyz339wWMIuVAzHIwikEdtDFYllMLwMhMZ4GTn55SBGjSn2ZQsDtCEDJ-zG7ptW6hC9n2UO2NVxaijERvURtBPFIE35EZPPSk_PAZHmQp72C0N6-h1xgfok9lb-9IV_hWorlegR8sD64p3SOy

The data contains the information needed to identify the contact and present a personalized web experience. When decrypted it looks something like this:

{
"contactid": "1c9b35d6-28d8-4520-8900-a89555f7f0c5",   
"email": "example@example.com",
"dateTime": "2018-07-11T11:19:54Z",
"discoveryKey" : "6iub35d6-34d8-6666-8900-e89100f7f0c5"
}
  • contactId: Unique ID for the contact in Engage, used for subsequent API-calls to get all relevant information about a contact

  • email: Contact's email address

  • id: Contact's key as defined by the project

  • dateTime: Specifies the time and date the link was generated (V.5)

  • discoveryKey: Contact's identifier for Voyado Elevate (optional)

Note

To include the discoveryKey from Voyado Elevate, ask your Voyado team to set it up for you (which they will do using email.autologin.includeDiscoveryKey).

Warning

Be careful about the access a user is granted by a soft login. A soft login link is a convenient way of identification but it can also be easily shared, for example by a user forwarding an email containing the link to a friend.

Decoding the message

  1. Extract the value string from the parameter “eClub”

  2. Decode 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. The initialization vector is stored in the first 16 bytes; the remainder is the encoded message

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

Encoding the message

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

  2. 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. The initialization vector and the encrypted string are concatenated, Base64 encoded and modified for URL inclusion (URL-safe Base64).

  4. 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.

C# decryption code example

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;
}

Perl decryption code example

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);

PHP decryption code example

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;
}

node.js code example

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);