> ## Documentation Index
> Fetch the complete documentation index at: https://developer.voyado.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Soft identification

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:

```javascript theme={null}
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:

```json theme={null}
{
"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.

<Warning>
  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.
</Warning>

## Decoding the message

<Steps>
  <Step title="Extracting the value">
    Extract the value string from the parameter “eClub”
  </Step>

  <Step title="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
  </Step>

  <Step title="Extracting the data">
    The initialization vector is stored in the first 16 bytes; the remainder is the encoded message"
  </Step>

  <Step title="Decrypting the message">
    With the shared key and initialization vector from step 3, decrypt the message using AES-256 (CBC)
  </Step>
</Steps>

## Encoding the message

<Steps>
  <Step title="Data format">
    The data is sent as a JSON with the structure shown in the example above.
  </Step>

  <Step title="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.
  </Step>

  <Step title="Preparing data">
    The initialization vector and the encrypted string are concatenated, Base64 encoded and modified for URL inclusion (URL-safe Base64).
  </Step>

  <Step title="Sending data">
    The complete encoded string is then sent as the query-string parameter “eClub”.
  </Step>
</Steps>

<Tip>
  The shared key used is 32 characters long, using no special characters, only a-z, A-Z, 0-9.
</Tip>

An example of such a key is: `DaqfT7Ys6tGqM3zbesEKpmacTJEqBCp2`.

There are online tools available for creating keys, such as [Strong Random Password Generator](https://passwordsgenerator.net/).

<AccordionGroup>
  <Accordion title="C# code example">
    ```csharp theme={null}
    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;
    }
    ```
  </Accordion>

  <Accordion title="Perl code example">
    ```perl theme={null}
    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);
    ```
  </Accordion>

  <Accordion title="PHP code example">
    ```php theme={null}
    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;
    }
    ```
  </Accordion>

  <Accordion title="node.js code example">
    ```javascript theme={null}
    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);
    ```
  </Accordion>
</AccordionGroup>
