API HMAC Authentication

Introduction

HMAC (hash-based message authentication code) is used to verify that a request is coming from an expected source and that the request has not been tampered with in transit. This is achieved by including both a public (key-identifier) and private key (key-secret) in each message, the latter of which is only known to the server and client. Using these values, the client will generate a unique HMAC (a hashed code) representing its request to the server. The client sends that HMAC to the server, along with a timestamp and all the arguments and values it was going to send anyway. The server (CrowdTwist) gets the request and re-generates its own unique HMAC based on the submitted values using the same methods the client used. The server (CrowdTwist) will then compare the two HMACs, if they are equal, then the server trusts the client, and processes the request.

1) HMAC does not require an API key in the request URL, but does use a key in the header. The key is the CrowdTwist public HMAC key.

2) All calls to api[client_id].crowdtwist.com are supported via HMAC, but calls to pos[client_id].crowdtwist.com are not supported.

3) HMAC is not supported on the Legacy Auth Sign-In and Sign-Out endpoints.

Creating a Request

Once HMAC has been enabled, all incoming API calls will require HMAC request headers, which will be detailed below.

  • X-CT-Authorization: This header must follow the convention “CTApiV2Auth [public key]:[signature]”, where [public key] is the HMAC public key and [signature] is the HMAC hash that requires several steps to generate.
  • Content-Type: This header must be “application/json” for POST and PUT requests.
  • X-CT-Timestamp: This header must be a UNIX timestamp and must be within 15 minutes of the current time. This timestamp is included to prevent replay attacks.
X-CT-Authorization: CTApiV2Auth publickey:signature
X-CT-Timestamp: 1505325876486
X-CT-Authorization: CTApiV2Auth publickey:signature
X-CT-Timestamp: 1505325876486

Creating the Signature

To create the [signature] value in the X-CT-Authorization header, several steps need to be followed.

  • [signature] = Base64(HMAC-SHA-256(PrivateKey, StringToSign ) )

The PrivateKey along with a StringToSign body will need to be HMAC-SHA-256 hashed. Then, that resulting hash will be Base64 encoded to create the signature. This signature is then added to the X-CT-Authorization header along with the public key as seen in the example.

To start, the StringToSign parameter will be created by appending the strings below separated by a new-line (“\n”) character.

StringToSign = 
HTTP-Verb + "\n" +
Content-MD5 + "\n" +
Content-Type + "\n" +
Timestamp + "\n" +
RequestURI
StringToSign = 
HTTP-Verb + "\n" +
Content-MD5 + "\n" +
Content-Type + "\n" +
Timestamp + "\n" +
RequestURI
  • HTTP Verb: the HTTP method (GET, POST,PUT, etc.)
  • Content-MD5: an MD5 hash of the exact request body (JSON Format)
  • Content-Type: Currently, the only value supported for POST/PUT requests is application/json
  • Timestamp: the UNIX timestamp
  • RequestURI: the URL with the query string parameters. This is not the full path, just the path and query string after domain.
    • For example, the RequestURI for https://api.crowdtwist.com/v2/activities, would be /v2/activities.

Sample PUT Request

To generate the signature the below process needs to be followed:

signature = Base64(HMAC-SHA-256(PrivateKey, StringToSign))

To create the StringToSign, the request body will need to be MD5 hashed.

{
“email_address”: “testing@crowdtwist.com”,
“postal_code”: “10010”,
“last_name”: “test”
}

MD5 value:

d8d84b98493543c9d51bda8c7c73f622

Next, the StringToSign is created by adding the MD5 hash.

StringToSign value:

PUT
d8d84b98493543c9d51bda8c7c73f622
application/json
1505759963477
/v2/users/11116703

After forming the StringToSign, the HMAC SHA-256 hashing algorithm using the private key is applied.

HMAC SHA-256 value:

3a9805afa6ec3850b80273c61a06a11702c38b868cc986e8d981b4a63d2356f2

To create the signature the above HMAC SHA-256 value will be Base64 encoded using UTF8.

Base 64 value:

M2E5ODA1YWZhNmVjMzg1MGI4MDI3M2M2MWEwNmExMTcwMmMzOGI4NjhjYzk4NmU4ZDk4MWI0YTYzZDIzNTZmMg==

The public key, and the signature are sent in the X-CT-Authorization header.

<!--?php
 
$curl = curl_init();
 
curl_setopt_array($curl, array(
  CURLOPT_URL =--> "https://api.crowdtwist.com/v2/users/11116703",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "PUT",
  CURLOPT_POSTFIELDS => "{\n  \"email_address\": \"testing@crowdtwist.com\",\n  \"postal_code\": \"10010\",\n  \"last_name\": \"test\"\n}",
  CURLOPT_HTTPHEADER => array(
    "cache-control: no-cache",
    "content-type: application/json",
    "x-ct-authorization: CTApiV2Auth publickey:signature",
    "x-ct-timestamp: 1505325876486"
  ),
));
 
$response = curl_exec($curl);
$err = curl_error($curl);
 
curl_close($curl);
 
if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}
<!--?php
$curl = curl_init();
curl_setopt_array($curl, array(
  CURLOPT_URL =--> "https://api.crowdtwist.com/v2/users/11116703",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "PUT",
  CURLOPT_POSTFIELDS => "{\n  \"email_address\": \"testing@crowdtwist.com\",\n  \"postal_code\": \"10010\",\n  \"last_name\": \"test\"\n}",
  CURLOPT_HTTPHEADER => array(
    "cache-control: no-cache",
    "content-type: application/json",
    "x-ct-authorization: CTApiV2Auth publickey:signature",
    "x-ct-timestamp: 1505325876486"
  ),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}

Sample Authentication Request

Following similar steps as seen above, a sample Authentication request will appear as follows:

POST https://api.crowdtwist.com/v2/user_auth_sign_in
 
Headers
X-CT-Authorization CTApiV2Auth ABCl3y7r0s5ukCXz5lCJOCrTZ427pjp5: YTUyNDU0MTc1YTg1MTZiN2IyMTc2Mzc5ZTA2YTlkN2Q1ZmEwNzAyYzM4ZmM0NWUzZWY2M2JmMWE1NzQ2YzBjMA==
Content-Type application/json
X-CT-Timestamp 1437604131
 
Body
{
    "redirect" : "http://test-rewards.crowdtwist.com",
    "username" : "AliceTwist",
    "verified" : 1
}
POST https://api.crowdtwist.com/v2/user_auth_sign_in
Headers
X-CT-Authorization CTApiV2Auth ABCl3y7r0s5ukCXz5lCJOCrTZ427pjp5: YTUyNDU0MTc1YTg1MTZiN2IyMTc2Mzc5ZTA2YTlkN2Q1ZmEwNzAyYzM4ZmM0NWUzZWY2M2JmMWE1NzQ2YzBjMA==
Content-Type application/json
X-CT-Timestamp 1437604131
 
Body
{
    "redirect" : "http://test-rewards.crowdtwist.com",
    "username" : "AliceTwist",
    "verified" : 1
}

First, to generate the StringToSign the above request body will be MD5 hashed to produce the below value:

  • de26bd80b53577dbe47738239d23f0b3

This value will then be added the StringToSign which will have the below format

POST
de26bd80b53577dbe47738239d23f0b3
application/json
1437604131
/v2/user_auth_sign_in

Taking the above StringToSign along with the private key (ABttp1b92Tb65445rmZL835f263n1q4Y) and applying the HMAC-SHA-256 algorithm will result in the below string:

  • a52454175a8516b7b2176379e06a9d7d5fa0702c38fc45e3ef63bf1a5746c0c0

Lastly, this value will then need to be Base64 encoded, resulting in the following value:

  • YTUyNDU0MTc1YTg1MTZiN2IyMTc2Mzc5ZTA2YTlkN2Q1ZmEwNzAyYzM4ZmM0NWUzZWY2M2JmMWE1NzQ2YzBjMA==

With the signature complete, the request can be sent with the appropriate HMAC Headers and a valid response will be returned.

Sample GET Request

Following similar steps as seen above, a sample GET request will appear as follows:

GET https://api.crowdtwist.com/v2/activities
 
Headers
X-CT-Authorization CTApiV2Auth ABCl3y7r0s5ukCXz5lCJOCrTZ427pjp5:YmQ0YTgyY2QzMTlhYmFiZTU3ZDBhODIyMDQ5YWU4OTg1MDI5ZjgyMjM3NTA5ZDNmMDkxYzgyY2JjN2E2OTQ1Yw==
X-CT-Timestamp 1437659826 
GET https://api.crowdtwist.com/v2/activities
 
Headers
X-CT-Authorization CTApiV2Auth ABCl3y7r0s5ukCXz5lCJOCrTZ427pjp5:YmQ0YTgyY2QzMTlhYmFiZTU3ZDBhODIyMDQ5YWU4OTg1MDI5ZjgyMjM3NTA5ZDNmMDkxYzgyY2JjN2E2OTQ1Yw==
X-CT-Timestamp 1437659826 

Since there is no request body, the StringToSign value can be immediately generated with the below format:

GET

1437659826
/v2/activities

Applying the HMAC-SHA-256 algorithm with the private key (ABttp1b92Tb65445rmZL835f263n1q4Y) and the string to sign results in the below string:

  • bd4a82cd319ababe57d0a822049ae8985029f82237509d3f091c82cbc7a6945c

Next, this hash is then Base64 encoded, which results in the following value:

  • YmQ0YTgyY2QzMTlhYmFiZTU3ZDBhODIyMDQ5YWU4OTg1MDI5ZjgyMjM3NTA5ZDNmMDkxYzgyY2JjN2E2OTQ1Yw==

This value is then placed in the X-CT-Authorization header as the [signature] to receive a valid response.

Error Conditions

Below are the possible HMAC specific error conditions; all responses are in JSON format.

Invalid HMAC header – Possibly due to missing http header or http header in unexpected format.

Response

{
  "error": "hmac_verification_failed",
  "message": "Invalid hmac header."
}
{
  "error": "hmac_verification_failed",
  "message": "Invalid hmac header."
}

HMAC signature mismatch – The client generated hash value and the server generated hash value does not match.

Response

{
  "error": "hmac_verification_failed",
  "message": "Hmac signature mismatch."
}
{
  "error": "hmac_verification_failed",
  "message": "Hmac signature mismatch."
}

HMAC timestamp expired – The client generated hash value and the server generated hash value matches, but the timestamp exceeded the acceptable limit. The timestamp must be within 15 minutes of the current time.

Response

{
  "error": "hmac_verification_failed",
  "message": "Hmac timestamp expired."
}
{
  "error": "hmac_verification_failed",
  "message": "Hmac timestamp expired."
}