Webhook signature verification

To ensure secure communication between Adfin and your platform, webhook signature validation is essential. This guide outlines how to generate the necessary security credentials, including a signature digest key and an authentication method, before creating a webhook.

📘

## Prerequisites

  1. You have set up webhooks for your app by subscribing to the webhook endpoint.

Step 1: Generate the webhook signature digest key

Before creating a webhook, you need to generate a signature digest key. This key will be used to validate the HMAC signature in each incoming webhook request, confirming the request’s authenticity and integrity.


curl -X PUT "https://staging.adfin.com/api/apps/{client_id}/webhooks/digest"

Explanation

  1. URL: Replace {client_id} with the unique client_id of your app.
  2. Response: This request will return a secret key that should be securely stored and used to validate incoming webhook signatures. Make sure this key is handled and stored with strict security measures.

Response example

{
    "secretKey": "_4ATIyq0Y8LyOGG_oxOXj8_9YqoGf64i1fmMPADeJkk_"
}

Step 2: Configure authentication for the webhook

You need to set up an authentication method for the webhook to ensure that only requests with valid credentials are accepted by your platform. Adfin supports several authentication types for webhook requests, including Basic Authentication, API Key Authentication, and OAuth Client Credentials.


Set the authentication method

curl -X PUT "https://staging.adfin.com/api/apps/{client_id}/webhooks/auth" \
-H "Content-Type: application/json" \
-d '{
  "basicAuthentication": {
    "username": "test",
    "password": "Test4321"
  },
  "apiKeyAuthentication": {
    "headerKey": "x-api-key",
    "headerValue": "testApiKey"
  }
}'

Explanation

  1. URL: Replace {client_id} with the unique client ID of your app.
  2. Headers:
    • Content-Type: Set to application/json for JSON payloads.
  3. Request Body:
    • basicAuthentication: Optional. Includes username and password fields.
    • apiKeyAuthentication: Optional. Defines headerKey (e.g., "x-api-key") and headerValue (the actual API key).
      You can configure one or more of these methods, depending on your platform’s security requirements.

Step 3: Verify incoming webhook requests

With the signature digest key and authentication method set up, you can now verify incoming webhook requests.


Webhook request headers

Every webhook request from Adfin includes these headers:

  • adfin-webhook-signature: The HMAC signature of the payload.
  • adfin-webhook-signature-timestamp: The timestamp of when the request was generated.

Example Headers:

adfin-webhook-signature: H63TRZj13EwxnpRFg454yLH92bm3jZyv31=
adfin-webhook-signature-timestamp: 2024-10-01T09:01:35Z

Signature Verification Process

To verify the webhook, follow these steps:

  1. Concatenate: Combine the request payload (body) and the signature timestamp (adfin-webhook-signature-timestamp).
  2. Generate HMAC: Use the signature digest key generated in Step 1 to create an HMAC SHA-256 hash of the concatenated data.
  3. Compare: Verify that the HMAC hash matches the adfin-webhook-signature header.

Java example for signature verification

Here’s a Java example to verify the signature:

private String generateVerificationSignature(String data, String key) {

		final String algorithm = "HmacSHA256";

		if (data == null || key == null) {
			throw new IllegalArgumentException("data and secretKey must not be null");
		}

		try {
			Mac hmacSHA256 = Mac.getInstance(algorithm);

			SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), algorithm);
			hmacSHA256.init(secretKeySpec);
			byte[] dataBytes = hmacSHA256.doFinal(data.getBytes(StandardCharsets.UTF_8));
			return new String(Base64.getEncoder().encode(dataBytes));
		} catch (NoSuchAlgorithmException | InvalidKeyException e) {
			throw new RuntimeException("Signature verification failed", e);
		}
	}

private boolean isSignatureValid(String signatureTimestampHeader, String signatureHeader, String eventPayload, String secretKey) {

		String SEPARATOR = "||";
		String data = signatureTimestampHeader + SEPARATOR + eventPayload;
		String verificationSignature = generateVerificationSignature(data, secretKey);
		return signatureHeader.equals(verificationSignature);
	}

Handling authentication in webhook requests

Based on the configured authentication method:

  • Basic Authentication: Check for Authorization: Basic base64(username:password).
  • API Key Authentication: Verify that the request includes the correct header and value pair (e.g., x-api-key: testApiKey).

Respond to the webhook

After validating the signature and authentication, respond to the webhook with a 2XX status code if successful. If validation fails, respond with an appropriate error code (e.g., 400 Bad Request or 401 Unauthorized).


Security best practices

  • Securely Store Credentials: Ensure both the signature digest key and authentication credentials are stored securely and rotated periodically.
  • Log Events: Log webhook validation attempts and responses for audit and troubleshooting.
  • Rate Limiting: Apply rate limiting to your webhook endpoint to prevent abuse.

What’s Next