How to Secure the Web API with Identity Verification

You can enable enhanced security for Prefinery's javascript snippet (and therefore the JavaScript Web API) by enforcing identity verification. Identity verification protects your customer’s information, and prevents bad actors from making guesswork or brute-force calls to the JavaScript Web API in order to phish personal information from users that may exist in your campaign. 

With this feature enabled, some Web API calls will require a signature to accompany the request to ensure its validity. Optionally, the user can be authenticated with the current browser session first so subsequent requests will each no longer require a signature until the user closes their browser. 

Identity verification is NOT REQURED to add a user (using the addUser method) to your project. The only time addUser would required identity verification is if it is being called with an email address which already exists in the campaign in which case the verification is required to prevent an attacker from fishing for customer data by entering email addresses that they do not own. If the email does not exist, the addUser request will succeed. If the email does exist then a 403 will be returned, which is up to you on how to handle or make use of such response (e.g. just catch the 403 in the front-end JS code and ignore it so that the end-user doesn't see it). 

Enabling Identity Verification

Identity Verification is enabled by default, but if you want to make sure that it is (in case you or a team member may have disabled it before) here's how you can enable it: 

We strongly recommend that you enforce identity verification.

1

From your project dashboard, navigate to Settings > Project Settings > General Settings

2

Scroll down and you should find the Enforce Identity Verification option. Turn it ON if it isn't yet and copy your Identity Verification Secret key. 

3

Proceed to the next sections below for more information on how to use your Identity Verification Secret key in generating a signature for Web API calls.    

Utilizing Identity Verification

Option 1: Authenticate the User

Call the authenticateUser function in order to authenticate the user into their current browser session (e.g. drop an authentication cookie) so that you do not have to provide a signature to each subsequent API request. This authentication will last until the user closes their browser.

Note: This is automatically done for you after adding a new user as part of the addUser function.

prefinery('authenticateUser', {
  email: 'bruce@wayneenterprises.com',
  signature: 'd1d2c2be5bd802b8625f5fabf274a7f65c11b89a6bb426e74161d79cb9f0e7f2'
}, function() {
  prefinery('getUser', {
    email: 'bruce@wayneenterprises.com',
  }, function(user) {
    console.log('User: ' + JSON.stringify(user));
  });
});

Option 2: Pass a Signature to Each Request

When making calls to the JavaScript Web API with Identity Verification enabled you will be required to pass a signature. The  signature is a generated SHA-256 HMAC of the combination of either the user's email address, referral code, or id, and your project's identity verification secret key. You should calculate this signature server-side (not client-side).

As an example, let's take the getUser function. To look up a user by email address, we'd use:

prefinery('getUser', {
  email: 'participant@email.com',
  signature: '8c1fea388d4ac3415adfa7c09a38d355855809ec5a473d54adfa1d708027907e'
}, function(user) {
  console.log('User: ' + JSON.stringify(user));
});

Where:

  • [Value to Compute Hash]: participant@email.com
  • [Identity Verification Secret Key]: aBc123DeF456gHi789JkL012mNo345PqR678sTu901Vw
  • Hashed Output / Signature: 8c1fea388d4ac3415adfa7c09a38d355855809ec5a473d54adfa1d708027907e

In this case, since we are looking up the user by email address, the HMAC is calculated using the email address value. If we were to look up the user by referral code, we would instead calculate the HMAC using the referral code value.

Generating the Signature

You can generate the HMAC  signature on your server (not client-side) using the following code, where USER-IDENTIFIER is either the email address, referral code, or id of the user.

Keep your secret key safe! Never commit it directly to your repository, client-side code, or anywhere a third party can find it.

Node.js

const crypto = require('crypto');

const signature = crypto
    .createHmac("sha256", "[PROJECT-SECRET]") // secret key (keep safe!)
    .update("[USER-IDENTIFIER]") // user's email, referral code, or id
    .digest("hex");

Ruby on Rails

require 'openssl'

OpenSSL::HMAC.hexdigest(
  'sha256', # hash function
  '[PROJECT-SECRET]', # secret key (keep safe!)
  '[USER-IDENTIFIER]' # user's email, referral code, or id
)

PHP

hash_hmac(
  'sha256', // hash function
  '[USER-IDENTIFIER]', // user's email, referral code, or id
  '[PROJECT-SECRET]' // secret key (keep safe!)
);

Django (Python 3)

import hmac
import hashlib

hmac.new(
  b'[PROJECT-SECRET]', # secret key (keep safe!)
  bytes('[USER-IDENTIFIER]', encoding='utf-8'), # user's email, referral code, or id
  digestmod=hashlib.sha256 # hash function
).hexdigest()

Troubleshooting

If you enable the Enforce Identity Verification feature, but do not sent a valid signature, then you will receive a HTTP 403 response.

Still need help? How can we help? How can we help?