Managed Consent Database

When a user has signed in on your application, your backend will serve a token to your frontend, which allows Transcend's SDK to save this authenticated user's consent preferences.

The token you'll serve is a JSON Web Token (JWT) containing a unique identifier for this user.

The identifier is encrypted on your backend server, placed in a JWT, and signed by your backend server. The encryption and signing keys can be found in the Admin Dashboard. More info about these keys can be found here.

The encryption algorithm is AES-KWP. The signing algorithm is HMAC-SHA-384.

import * as crypto from 'crypto';
import * as jwt from 'jsonwebtoken';
function createConsentToken(
userId: string,
base64EncryptionKey: string,
base64SigningKey: string
): string {
// Read on for where to find these keys
const signingKey = Buffer.from(base64SigningKey, 'base64');
const encryptionKey = Buffer.from(base64EncryptionKey, 'base64');
// NIST's AES-KWP implementation { aes 48 } - see https://tools.ietf.org/html/rfc5649
const encryptionAlgorithm = 'id-aes256-wrap-pad';
// Initial Value for AES-KWP integrity check - see https://tools.ietf.org/html/rfc5649#section-3
const iv = Buffer.from('A65959A6', 'hex');
// Set up encryption algorithm
const cipher = crypto.createCipheriv(encryptionAlgorithm, encryptionKey, iv);
// Encrypt the userId and base64-encode the result
const encryptedIdentifier = Buffer.concat([
cipher.update(userId),
cipher.final(),
]).toString('base64');
// Create the JWT content
const jwtPayload = {
encryptedIdentifier,
};
// Create a JSON web token and HMAC it with SHA-384
const consentToken = jwt.sign(jwtPayload, signingKey, {
algorithm: 'HS384',
});
return consentToken;
}

N.B. pip install cryptography pyjwt

import base64
import jwt
from cryptography.hazmat.primitives.keywrap import aes_key_wrap_with_padding
def create_consent_token(user_id: str, base64_encryption_key: str, base64_signing_key: str) -> str:
# Read on for where to find these keys
encryption_key = base64.b64decode(base64_encryption_key)
signing_key = base64.b64decode(base64_signing_key)
# encode user_id as bytes
user_id_bytes = str.encode(user_id)
# NIST's AES-KWP implementation { aes 48 } - see https://tools.ietf.org/html/rfc5649
encrypted_identifier = aes_key_wrap_with_padding(encryption_key, user_id_bytes)
# base64-encode the result
base64_encrypted_identifier = base64.b64encode(encrypted_identifier)
# Create the JWT content
jwt_payload = { 'encryptedIdentifier': base64_encrypted_identifier.decode() }
# Create a JSON web token and HMAC it with SHA-384
consent_token = jwt.encode(jwt_payload, signing_key, algorithm="HS384")
return consent_token

Your server will generate the token and return it to the frontend (likely as part of the response payload for the login flow):

// When a user has authenticated
const consentToken = createConsentToken(
userId,
base64EncryptionKey,
base64SigningKey
);
return {
status: 200,
body: {
user: authenticatedUserData,
consentToken,
}
}

On the frontend, you will then pass this token to Transcend:

// Authenticate the user to airgap, and sync their consent with Transcend's Managed Consent Database
airgap.sync({ auth: consentToken });

The keys above, encryptionKey and signingKey, can be retrieved from the Admin Dashboard, under the "Managed Consent Database" section on the Developer Settings page.

  1. Toggle "on" the Managed Consent Database feature (see screenshot)
  2. Click on View Encryption Key to see the encryptionKey in the code example above. Save the contents somewhere safe!
  3. Click on View JWT Signing Key to see the signingKey in the code example above. Again, please save the contents somewhere safe!

  1. Make sure Reporting Only mode is off
  2. Your domain is included in the Domains List (e.g. localhost:3033 if you're using the backend-consent-example)
  3. Navigate to Regional Experiences and make sure that you are in a region with an experience defined. (e.g. if you are in New York, set an experience using either the region of New York or the New York time zone (or both) and add a purpose (e.g. Advertising) to it.)
  4. If you haven't already, copy the HTML Snippet into the page you want to test this on (using the Test bundle)

Now that you've collected the user consent from the web, you may need to look up their consent record as part of executing backend data processes. This can be easily done using our Sombra API.

Endpoint reference: Query User Consent

Your backend server may need to gate tracking/data-processing logic based on the user consent preferences. An example of how to do so for user with email 'foo@example.com':

const request = require('request');
const result = request.post(
'{{yourOrganizationSombraURL}}/v1/consent-preferences',
{
headers: {
authorization: 'Bearer {{apiKey}}',
'x-sombra-authorization': 'Bearer {{sombraApiKey}}',
'content-type': 'application/json',
},
body: {
filterBy: {
identifiers: 'foo@example.com',
// grab this value from the Admin Dashboard > Consent > Developer Settings
consentManagerId: 'ee1a0845-694e-4820-9d51-50c7d0a23467',
},
},
json: true,
}
);
const consentPreference = result.nodes[0];
// May be undefined/empty list if the user has never given consent
if (consentPreference?.purposes?.Advertising) {
// The user has given consent to Advertising, perform some custom logic...
}
if (consentPreferences?.purposes?.Functional !== false) {
// The user has NOT explicitly opted out of Functional tracking purpose, perform some custom logic...
}

A common scenario: your company uploads a list of users to Facebook once a week, and you need to filter this list to exclude users who have opted out of targeted advertising. Here's an example of how you might do so:

const request = require('request');
const { nodes: userConsentPreferences } = request.post(
'{{yourOrganizationSombraURL}}/v1/consent-preferences',
{
headers: {
authorization: 'Bearer {{apiKey}}',
'x-sombra-authorization': 'Bearer {{sombraApiKey}}',
'content-type': 'application/json',
},
body: {
filterBy: {
identifiers: userIdentifiers,
// grab this value from the Admin Dashboard > Consent > Developer Settings
consentManagerId: 'ee1a0845-694e-4820-9d51-50c7d0a23467',
},
},
json: true,
}
);
const usersToUpload = userIdentifiers.filter((identifier) =>
userConsentPreferences.find(
(data) =>
data.userId === identifier &&
// This logic makes sure that the user has not explicitly opted out
// You may also write your own logic here to check for explicit consent (opt-in) as well
data.purposes?.Advertising !== false &&
data.purposes?.SaleOfInfo !== false
)
);
// Now you can safely upload `usersToUpload`