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.