Authentication/Authorization Protocol


Why Use Authentication/Authorization?

Your users’ privacy is paramount. Data breaches and insecure security practices are severe risks to all companies that conduct business online. Our technology, in conjunction with your website and apps, store privately captured images of your users. We need to take the necessary precautions to ensure this is managed securely.

All calls to our API must be signed via the Ditto Authentication/Authorization protocol in order to succeed. This protocol protects against a number of attack vectors that a bad actor might pursue:

  • Digital signature reuse
  • User impersonation
  • Brute force key identification
  • Brute force ditto_id identification
  • Unwarranted user data modification retrieval

We have extensive documentation below on how to implement authentication/authorization and all of our SDKs are compatible.

Overview

All endpoints across the Try-On, Face Insights, and Frame Recommendations REST APIs are secured by our authentication/authorization protocol as laid out below. Every endpoint and method requires the caller to follow this protocol.

Three pieces of information are necessary to follow the protocol: Name, Access Key ID, and Secret Access Key. The Name and Secret Access Key will be used to generate the required signatures. The Access Key ID will be sent along with the signatures in the API requests.

Name Description Example
Name (name) A human readable key used as the "message" in signature creation; either a partnerId or scanId depending on the API endpoint 'ditto'
Access Key ID (access_key_id) A hex string used as a look up for the secret key '48f92d026aa0abb6'
Secret Access Key (secret_access_key) A randomly generated 64 bytes. 512 bits / 8 bits in a byte = 64 bytes. Stored as a 128 character hex string. '3e96e04f56659c58d621c23b048814a 962ff6fec68cd5efb0ee09fdd8211d2387 8e3424f16c89e7bb64e19fe77bce83c345 9724081f79e66d933905a1fcf4d65'

Secret Access Keys should never be shared with anyone or passed in cleartext, such as, emails, slack message, or text messages! We will provide these through a shared 1Password vault—please exercise caution! For an invite to your 1Password vault, please reach out to your CSM.

In order to authenticate a call, you must provide a signature and an access_key_id . See below on how to generate a signature. These are passed via HTTP headers (‘X-Ditto-Access-Key-Id’ and ‘X-Ditto-Signature’) with each API request. More on this in the HTTP Headers section.

Name Description Example
Message String to be hashed. Refer to the table below to understand which message to pass given the endpoint you're calling 'this_is_my_message', 'partnerId', '30484433cebe1cbf381616704d7617a0ff178cf6'
Timestamp Integer value of seconds since unix utc epoch (1/1/1970) converted to string '1491326655'
Separator A period is used to separate certain elements passed into a header (see below) '.'

The following code snippets are in Python, but can be implemented in any language. If you are having any trouble, don’t hesitate to reach out to someone on the Ditto team.

Create Timestamp

create_timestamp
#find seconds since unix utx epoch and convert to string

import time
timestamp = str(int(time.time()))

print timestamp
1491327401

Create SHA-512 String (to be hashed)

See The ‘message’ in Signature Creation below for what to use as the “message”.

create_sha512_string
#combine message + separator + timestamp

SEPARATOR = '.'
message = 'this_is_my_message'

text = message + SEPARATOR + timestamp

print text
'this_is_my_message.1491327401'
Generate SHA-512 Hash
#take combined text and keys and create hash
#secret_access_key provided securely by DITTO
#text from create_sha512_string script above

import hmac
from hashlib import sha512

hash = hmac.new(secret, text, sha512).digest()

Format Signature

format_signature
#take text and combine with a websafe base64 encoded hash
#websafe base64 is defined as base64 with + swapped with - and / swapped with _ and all = at the end #removed.

import base64

safe_hash = base64.urlsafe_b64encode(hash).rstrip('=')
signature = text + SEPARATOR + safe_hash

print signature
this_is_my_message.1491327401.wiGdWGngUyw7tNbpdJU4Xk5Gl0MfgbFPESu9bpoMbQK8N-5w5xp_43xEvxckCQ624KjddqyfzcqNccNRYQ7jAw

The ‘message’ in Signature Creation

Which ‘message’ to use in generating a signature varies by endpoint. The following tables map the endpoints to their messages:

Browser SDK Object Message
Ditto.Scan({ partnerSignature }) partnerId
Ditto.Overlay({ overlaySignature }) scanId
Ditto.Overlay({ modeloverlaySignature }) Try-On Model's scanId (e.g. ditto_model_female_01)
Ditto.getFrontalFrame({ overlaySignature }) scanId
Ditto.getProducts({ partnerSignature }) partnerId
Try-On API Endpoints Message
/api/1.3/products/ partnerId
/api/1.3/dittos/ scanId
/api/1.3/dittos/{scanId}/ scanId
/api/1.3/dittos/{scanId}/strip/ scanId
/api/1.3/dittos/{scanId}/frontal_frame/ scanId
Face Insights API Endpoints Message
/analyses/{scanId}/images/frontal/ scanId
/analyses/{scanId}/client-data/{data_type}/ scanId
/analyses/{scanId}/ scanId
/analyses/{scanId}/status/{service}/ scanId
/analyses/{scanId}/results/{service}/ scanId
Frame Recommendations API Endpoint Message
POST /recommendation/ partnerId

HTTP Headers

Each API call is authenticated by passing two HTTP headers as described below.

Header Description
X-Ditto-Signature This is a string which contains the signature that was generated via the protocol described above
X-Ditto-Access-Key-Id This is a string which contains the access_key_id provided by Ditto via 1Password

Error Messages

If authentication is not provided or incorrect, you will receive an HTTP Error 403 - Forbidden response.

Implementations

We’ve put together examples in Python, PHP, js/node, and C#.

Python
import base64
import binascii
import hmac
import time

from hashlib import sha512


def get_signature(message, secret):
  timestamp = str(int(time.time()))
  stamped_message = message + '.' + timestamp
  secret_bin = binascii.unhexlify(secret)
  hash = hmac.new(secret_bin, stamped_message, sha512).digest()
  hash_websafe = base64.urlsafe_b64encode(hash).rstrip('=')
  signature = stamped_message + '.' + hash_websafe
  return signature

secret = 'babb23b3bb4b234b32b4babcf987239847bacba987ac987ac879a87c'
message = 'user_ping_test' ##specifying a scanID as the 'message' will create an OverlaySignature ; use a partnerID for a PartnerSignature

signature = get_signature(message, secret)