Authenticating Your Requests

Successfully authenticating your requests is the first step to an integration.

Table of Contents


How to authenticate

All requests to the Cognito servers must be authenticated. Cognito uses a request signature system that is formed according to Section 3 in “Signing HTTP Messages. This authentication method provides a multitude of benefits including only requiring you to transmit one of your two secrets over the wire.

Using well-tested and supported crypto libs for your language is imperative. Below we have compiled links to some recommended functions and libraries for the most common languages.

LanguageBase64SHA256HMAC-SHA256
rubyExampleExampleExample
PythonExampleExampleExample
PHPExampleExampleExample
NodeExampleExampleExample
JavaExampleExampleExample

In order to authenticate your requests, you must include Date, Digest, and Authorization headers. Now let’s take a look at how each of these components is constructed:

Date

The time you submit your request. This must conform to the W3C’s date header format. The full header must look something like:

Date: Thu, 25 Aug 2016 22:37:14 GMT

Digest

To form the digest, take your request body and run it through SHA256. Then take this value and get its base64 value. The full header must look something like:

Digest: SHA-256=gUsPgGzkEGk1T7ieLCO4sMimZaDzh9our5oy/qvkQKs=

Authorization

The first step is to construct what is known as the (request-target). This is formed using the lowercased HTTP method used for this endpoint as well as the request path and any accompanying query parameters. For instance, post /profiles. If you include any query parameters, this would look like post /profiles?foo=bar

The second step is to construct a signature string based on the following template using all of the components you have already determined:

(request-target): post /profiles
date: Thu, 25 Aug 2016 22:37:14 GMT
digest: SHA-256=gUsPgGzkEGk1T7ieLCO4sMimZaDzh9our5oy/qvkQKs=

This value should be run through HMAC-SHA256 using your organization’s API secret as the key and then base64. (Note: You must use the literal character bytes of your API secret. It is not a hex-encoded byte string). The final step is to take everything and compose the authorization header:

Authorization: Signature keyId="your-api-key",algorithm="hmac-sha256",headers="(request-target) date digest",signature="your-signature"

Putting it all together

Using our profile creation endpoint as an example, a full request should end up looking something like this:

POST https://sandbox.cognitohq.com/profiles HTTP/1.1
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json
Cognito-Version: 2016-09-01
Authorization: Signature keyId="sandbox_key_11111111111111111111111111111111",algorithm="hmac-sha256",headers="(request-target) date digest",signature="E+DcxQOjN3RDGQZmm6JfBt8ADwpGUQFxan7Ok+qenJc="
Date: Thu, 25 Aug 2016 22:37:14 GMT
Digest: SHA-256=gUsPgGzkEGk1T7ieLCO4sMimZaDzh9our5oy/qvkQKs=

Ruby example

Below is a full example request programmed in Ruby.

require 'http' # https://github.com/httprb/http
require 'digest'
require 'base64'
require 'openssl'

API_KEY     = 'your-api-key'
API_SECRET  = 'your-api-secret'

# We'll be creating a profile in this example
body = JSON.dump({ data: { type: 'profile' } })
request_target = 'post /profiles'

# Generates a digest using the request body
digest = 'SHA-256=' + Base64.strict_encode64(Digest::SHA256.digest(body))

# Generates a date in this format: Thu, 25 Aug 2016 22:37:14 GMT
date = Time.now.httpdate

# Declares that our body is JSON
content_type = 'application/vnd.api+json'
accept_type  = 'application/vnd.api+json'
version      = '2016-09-01'

# Generates the signing string. Note that the parts of the string are
# concatenated with a newline character
signing_string = [
  "(request-target): #{request_target}",
  "date: #{date}",
  "digest: #{digest}"
].join("\n")

# Creates the HMAC-SHA256 digest using the API secret and then base64
# encodes that value
signature = Base64.strict_encode64(
  OpenSSL::HMAC.digest(
    OpenSSL::Digest::SHA256.new, API_SECRET, signing_string
  )
)

# Creates the authorization header and concatenates it together using
# a comma
authorization = [
  'Signature keyId="' + API_KEY + '"',
  'algorithm="hmac-sha256"',
  'headers="(request-target) date digest"',
  'signature="' + signature + '"'
].join(',')

# Put everything together and execute the request. Note that the headers
# are defined in the same order as they are defined in the Authorization
# header above. They can be in any order, but they must be consistent.
response =
  HTTP.headers(
    'Date'             => date,
    'Digest'           => digest,
    'Authorization'    => authorization,
    'Content-Type'     => content_type,
    'Accept'           => accept_type,
    'Cognito-Version'  => version
  ).post('https://sandbox.cognitohq.com/profiles', body: body)

Potential pitfalls

  • Incorrectly ordering the headers. The headers defined in your request should be sent in the same order as you define them in the Authorization header. They can be in whatever order you want, they should just be consistent.
  • Having a trailing newline on Base64 encodings – Some Base64 libraries automatically add a newline to the end of the encoding. Make sure to remove it if yours does this.
  • Using inconsistent dates – Make sure to store the date you use in the Date header so you can use the exact same time when constructing the signing string.
  • Automatic HTTP library redirection – The identity search endpoint (seen in a later guide) will redirect your request in certain cases. If your HTTP library automatically redirects, this can cause an authentication error since the signature will need to be recomputed for the new request target.

Next steps

You’re all set to securely run requests using Cognito. The Authorization and accompanying headers will be omitted from guides going forward for brevity. Next you should read our guide on verifying your first customer.