Implementing a Signature Hash and Validating an Open Channel Webhook

This article assumes you have created have or know how to create a webhook on your server. Go here for more information on Setting Up Your Webhook.

This article will walk you through the steps required to create a signature hash to authenticate open channel API calls sent by Airship to your Webhook:

When configuring your webhook in the Airship Dashboard and selecting "Signature Hash" from the Authentication dropdown menu, you will provide us with a secret key based on the sha256 hash function, which you will later use to verify the signature on the receiving server.

You can generate a strong Signature Secret with the following commands:

On Linux:

openssl rand 128 | sha256sum | cut -f1 -d' '

On Mac:

openssl rand 128 | shasum -a 256 | cut -f1 -d' '

Other platforms may differ slightly.

Ensure your shared Signature Secret is stored securely in your system.

Each call made from Airship to your webhook will include the X-UA-TIMESTAMP and X-UA-SIGNATURE headers, and the request body. For example:

POST /yourWebhookServer/push HTTP/1.1 (or - GET /yourWebhookServer/validate)
Content-Type: application/vnd.urbanairship+json; version=3;
Content-encoding: gzip
X-UA-TIMESTAMP: 1536947409
X-UA-SIGNATURE: 68688b9dbd5c381851d3cd51dba3093c6633ceef58e6fee6ad4757f857f59aea
Data-Attribute: values
  • The X-UA-TIMESTAMP is an Epoch Unix time in seconds representing when the message was sent from Airship.
  • To calculate the Signature, Airship first generates a Message. The Message is a concatenation of the string values of the X-UA-TIMESTAMP header and the request body JSON, separated by the character ":". 
    • All values are UTF-8 encoded.
    • Since the /validate call is a GET, the request body JSON is represented by an empty string when calculating the signature
    • For /push, the request body will be the JSON of the Open Delivery Payload
  • The X-UA-SIGNATURE header is a sha256 hash of the Message string described above, using the secret key stored in Airship during setup. After hashing, the final value of the header is represented as a hexadecimal string.

To validate within the webhook, you will need to receive the API request coming from Airship and use the values of the X-UA-TIMESTAMP header and the request Body to generate the expected signature.  Then, compare your calculated signature to the X-UA-SIGNATURE header on the request in order to authenticate it.

The following is an example of a function that would validate the signature in the incoming call:

Note that this example is written in NodeJS and has nuances specific to Node. Implementation code will vary depending on the language or library you will use.
function isValidSignature(request, res) {
var key = {secret key provided to Airship in Webshook setup in the Dashboard}; var headers = request.headers; var signature = headers['x-ua-signature']; var body = ''; // Create an empty vairable for the Body // If request body isn't empty, assign value to variable if (request.body) { body = request.body; } // Note that Node http requires using lower-cases for all headers var message=headers['x-ua-timestamp']+":"+body; // Concatenation of recieved timestamp and body var hmac = crypto.createHmac('sha256', key) // Create a sha256 hashed code using the secret key hmac.update(message, 'utf8') // Update the hash with the concatantaed message using utf8 var digest = hmac.digest('hex'); return (signature === digest); // If signature equals your hased code, return true }

An advantage of the above formulation of this validation function is that it gets the elements from the API call which then works for both the call to the /validate/ and /push/ endpoints with the same function.

Additionally, you should validate the X-UA-TIMESTAMP against the current time. We recommend that you use a 5-minute threshold to account for time drift, though Airship uses NTP and we recommend that your webhook server does the same. To prevent timing attacks, you should employ a constant time-compare function when checking signatures.

Return to the Webhook configuration in your Airship Dashboard, Check the Enabled box to enable the open channel for use, then click Update. This will send out the GET call to your server's /validate/ endpoint. In response to this call your server should then:

  • Return a 200 response code.
  • Return a Content-Type of "application/json".
  • Return a JSON body with the confirmation code in the following format: {"confirmation_code":"559384cd-6284-4e3e-9e4e-7c260019a251"}.

Be sure that the body is valid JSON and returns the confirmation_code generated for the webhook in the Airship Dashboard.

This will complete the process and your webhook will be ready to receive POST requests.