Verify webhook signature

How to register webhook endpoints on Transcend and authenticate incoming requests.

There are two types of webhooks that you can receive from Transcend:

  1. Identity enrichment webhooks
  2. Server Webhook Integration

Each of these webhooks uses the same form of authentication.

Set up a route on your server

Receive a webhook by creating a route that listens for new POST requests at your pre-registered URL (please refer to either of the above guides to learn about registering your webhook URL). The webhook will contain a JSON payload in the request body and a header, x-sombra-token, containing an asymmetrically signed token.

Verify the webhook signature

Endpoint reference: JWT public key

Webhooks will always be signed with an asymmetric digital signature algorithm. This is a JSON Web Token → that is signed with a private key. You will verify the token with a public key that can be on your Sombra gateway.

  • Choose a JWT library for your language. It must support ES384.
  • Read the HTTP request header x-sombra-token.
  • Read the public key from Sombra at the path /public-keys/sombra-general-signing-key. We recommend caching this value. Note this is an authenticated route. You can read about making a request to our API here.
  • Using the verify function with the algorithm ES384, verify x-sombra-token against the public key.

🚧

Things to note

  • Signature verification is an important security feature that ensures webhooks are coming from Transcend. Always make sure the signature is valid before proceeding with any operation.

  • It's important that you only use ES384 as the signature verification algorithm. Allowing other algorithms can open security vulnerabilities.

  • You can either hard code or programmatically read the public key. Both the public key as well as the public key URL are available in the Admin Dashboard. We will notify you in advance of key rotations.

Please refer to either of the guides listed at the top to learn about responding to each type of webhook.

Sample code

// Libraries
const got = require('got');
const jwt = require('jsonwebtoken');

// URL of your sombra gateway
const SOMBRA_URL = '<<yourOrganizationSombraURL>>';

// API key for Transcend API issued at https://app.transcend.io/settings#Developer
// NOTE: this is a secret, please handle with care
const TRANSCEND_API_KEY = '~~SECRET-TRANSCEND-API-KEY-SECRET~~';

// Only needed if on premise gateway uses bearer auth
// NOTE: this is a secret, please handle with care
const SOMBRA_API_KEY = '~~SECRET-SOMBRA-API-KEY-SECRET~~';

// Global to cache the webhook signing public key
let cachedPublicKey;

/**
 * Helper to verify incoming webhook requests
 *
 * @param {string} signedToken - the `x-sombra-token` header, which is a JSON
 * Web Token asymetrically signed with ES384.
 *
 * @returns {Object} - the signed body
 */
module.exports = async function verifyAndExtractWebhook(signedToken) {
  // Get the public key and cache it for next time.
  if (!cachedPublicKey) {
    try {
      const response = await got.get(`${SOMBRA_URL}/public-keys/sombra-general-signing-key`, {
        headers: {
          authorization: `Bearer ${TRANSCEND_API_KEY}`,
          // only needed if using on-premise sombra with bearer auth enabled
          'x-sombra-authorization': SOMBRA_API_KEY ? `Bearer ${SOMBRA_API_KEY}` : undefined,
        },
      });
      cachedPublicKey = response.body;
    } catch (err) {
      console.error('Failed to get public key:', err);
    }
  }

  // Verify webhook signature with the public key (ensures that Transcend sent the request)
  const signedBody = jwt.verify(signedToken, cachedPublicKey, {
    // important to specify the algorithm
    algorithms: ['ES384'],
  });

  return signedBody;
};

👍

Check out our code examples

We have working examples on GitHub.