How to Secure Webhook Endpoints Using Authentication in Adyen Salesforce Commerce Cloud

Secure your Adyen webhook endpoints by configuring Basic Authentication credentials and optional HMAC signature verification through site preferences, then validate incoming requests using the checkNotificationAuth module before processing any notification data.

The adyen-salesforce-commerce-cloud integration provides a defense-in-depth approach to protect webhook endpoints from unauthorized access and payload tampering. By implementing both Basic Authentication and HMAC signature verification, you ensure that only legitimate Adyen notifications reach your Commerce Cloud storefront. This guide explains how to configure and implement these security mechanisms using the actual source code from the repository.

Authentication Mechanisms Overview

The integration protects the webhook entry point (notify.js) with two complementary security layers that work together to verify the identity of the caller and the integrity of the payload.

Basic Authentication

Basic Authentication prevents unauthenticated callers from invoking the notification controller by requiring valid credentials in the HTTP Authorization header. The system reads the Adyen_notification_user and Adyen_notification_password site preferences and validates them against the incoming request using AuthenticationUtils.checkGivenCredentials.

HMAC Signature Verification

HMAC Signature Verification (optional but recommended) guarantees the integrity and origin of the notification payload. When configured with an HMAC key, the system rebuilds the notification payload, computes a signature using the merchant-provided key, and performs a constant-time comparison with the additionalData.hmacSignature sent by Adyen to prevent timing attacks.

Configuring the Credentials

Before implementing the code-level checks, you must define the authentication credentials in Business Manager:

  1. Navigate to Administration → Site Preferences → Custom Preferences in Salesforce Business Manager.

  2. Configure the following preferences for your site:

Preference Type Description
Adyen_notification_user String The username that Adyen will send in the Basic Auth header.
Adyen_notification_password String The matching password for Basic Auth validation.
Adyen_hmac_key String Hex-encoded HMAC key used for optional signature verification.

These values are read at runtime via Site.getCurrent().getCustomPreferenceValue() in checkNotificationAuth.js (lines 26-31).

Where Authentication Checks Are Triggered

The webhook controller notify.js invokes the authentication helper functions before processing any notification data:

// src/cartridges/int_adyen_webhooks/cartridge/notify.js
const checkAuth = require('*/cartridge/checkNotificationAuth');

function notify(req, res, next) {
  try {
    // 1. Basic auth validation
    const status = checkAuth.check(req);

    // 2. Optional HMAC validation (if HMAC key is set)
    const hmacKey = AdyenConfigs.getAdyenHmacKey();
    const isHmacValid = handleHmacVerification(hmacKey, req);

    if (!status || !isHmacValid) {
      // Reject the request with HTTP 403
      res.status(403).render('/adyen/error');
      return {};
    }
    // ... process valid notification
  }
}

If either check fails, the controller returns a 403 Forbidden response and aborts further processing, ensuring that only authenticated, untampered notifications are accepted.

How Basic Auth Verification Works

The checkGivenCredentials function in libAuthenticationUtils.js decodes the Authorization: Basic ... header, splits the credentials on the colon delimiter, and compares them with the stored site preferences:

// libAuthenticationUtils.js – checkGivenCredentials
function checkGivenCredentials(baHeader, baUser, baPassword) {
  const basicPrefix = 'Basic';
  if (baHeader && baHeader.indexOf(basicPrefix) === 0) {
    const base64Credentials = baHeader.substring(basicPrefix.length).trim();
    const credentials = StringUtils.decodeBase64(base64Credentials);
    const values = credentials.split(':', 2);
    return values[0] === baUser && values[1] === baPassword;
  }
  return false;
}

This function is called by checkNotificationAuth.check(req), which extracts the header from the request and retrieves the configured credentials from site preferences.

How HMAC Verification Works

When an HMAC key is present, checkNotificationAuth.validateHmacSignature reconstructs the exact payload that Adyen signs, computes the expected signature, and performs a constant-time comparison to mitigate timing attacks:

// checkNotificationAuth.js – validateHmacSignature
function validateHmacSignature(request) {
  const notificationData = request.form;
  const hmacSignature = notificationData['additionalData.hmacSignature'];
  const merchantSignature = AuthenticationUtils.calculateHmacSignature(request);
  if (compareHmac(hmacSignature, merchantSignature)) {
    return true;
  }
  AdyenLogs.error_log('HMAC signatures mismatch, the notification request is not valid');
  return false;
}

The helper calculateHmacSignature (in libAuthenticationUtils.js) creates the payload from notification fields, signs it with the HMAC key using the AdyenConfigs.getAdyenHmacKey() value, and returns a Base64-encoded string for comparison.

Code Examples for Custom Implementations

Adding Basic Auth to a Custom Webhook Controller

If you need to create additional webhook endpoints, reuse the existing authentication module:

// myCustomWebhook.js
const checkAuth = require('*/cartridge/checkNotificationAuth');

function myWebhook(req, res, next) {
  if (!checkAuth.check(req)) {
    // Reject unauthenticated calls
    res.status(403).render('/adyen/error');
    return next();
  }

  // ...process the payload...
  res.render('/mySuccessPage');
  return next();
}
module.exports = myWebhook;

Enforcing HMAC Verification Only

For scenarios where you want to verify payload integrity without Basic Auth:

// hmacOnlyWebhook.js
const checkAuth = require('*/cartridge/checkNotificationAuth');
const AdyenConfigs = require('*/cartridge/adyen/utils/adyenConfigs');

function hmacOnly(req, res, next) {
  const hmacKey = AdyenConfigs.getAdyenHmacKey();
  if (hmacKey && !checkAuth.validateHmacSignature(req)) {
    res.status(403).render('/adyen/error');
    return next();
  }

  // ...payload handling...
  res.render('/hmacSuccess');
  return next();
}
module.exports = hmacOnly;

Testing the Authentication Flow

Use the following Jest example to verify your webhook controller rejects unauthenticated requests:

// __tests__/myCustomWebhook.test.js
const myWebhook = require('*/cartridge/myCustomWebhook');
const checkAuth = require('*/cartridge/checkNotificationAuth');

jest.mock('*/cartridge/checkNotificationAuth');

test('rejects request without valid Basic Auth', () => {
  const req = { httpHeaders: {} };
  const res = { status: jest.fn(() => res), render: jest.fn() };
  checkAuth.check.mockReturnValue(false);

  myWebhook(req, res, jest.fn());

  expect(res.status).toHaveBeenCalledWith(403);
  expect(res.render).toHaveBeenCalledWith('/adyen/error');
});

Key Files in the Authentication Flow

File Purpose
src/cartridges/int_adyen_webhooks/cartridge/notify.js Entry controller that receives Adyen notifications and invokes authentication checks.
src/cartridges/int_adyen_webhooks/cartridge/checkNotificationAuth.js Implements Basic Auth verification (check) and optional HMAC signature validation (validateHmacSignature).
src/cartridges/int_adyen_webhooks/cartridge/libs/libAuthenticationUtils.js Utility library containing checkGivenCredentials and calculateHmacSignature functions.
src/cartridges/int_adyen_webhooks/cartridge/handleNotify.js Core business logic that updates orders based on the notification payload (executed only after auth passes).
metadata/site_import/custom-objecttype-definitions.xml Defines the site preferences (Adyen_notification_user, Adyen_notification_password, Adyen_hmac_key) used by authentication scripts.

Summary

  • Basic Authentication protects webhook endpoints by validating credentials stored in Adyen_notification_user and Adyen_notification_password site preferences against the HTTP Authorization header.
  • HMAC Signature Verification provides cryptographic proof of payload integrity by comparing the Adyen-provided signature with a locally computed signature using the Adyen_hmac_key preference.
  • The checkNotificationAuth.js module exposes check() for Basic Auth and validateHmacSignature() for HMAC validation, both used by notify.js before processing any notifications.
  • Failed authentication attempts return HTTP 403 Forbidden, preventing unauthorized access to order management logic in handleNotify.js.
  • All credentials are configured via Business Manager site preferences and read at runtime using Site.getCurrent().getCustomPreferenceValue().

Frequently Asked Questions

What site preferences are required to secure webhook endpoints?

You must configure Adyen_notification_user and Adyen_notification_password for Basic Authentication. Optionally, set Adyen_hmac_key to enable HMAC signature verification for additional payload integrity protection. These preferences are defined in the metadata site import files and accessed via Site.getCurrent().getCustomPreferenceValue() in the authentication scripts.

How does the HMAC signature prevent tampering?

The HMAC signature ensures payload integrity by cryptographically signing the notification data. The validateHmacSignature function in checkNotificationAuth.js reconstructs the payload exactly as Adyen signed it, computes the expected signature using your merchant-specific HMAC key, and performs a constant-time comparison with the additionalData.hmacSignature field. If the payload was modified in transit, the signatures will mismatch and the request will be rejected with a 403 error.

Can I use only HMAC verification without Basic Auth?

Yes, you can implement HMAC-only verification by calling checkAuth.validateHmacSignature(req) directly without invoking checkAuth.check(req). However, the standard implementation in notify.js uses both mechanisms for defense-in-depth. If you choose to use only HMAC verification, ensure your hmacKey check handles cases where the key might not be configured to avoid rejecting legitimate notifications.

Where are the authentication checks triggered in the codebase?

The authentication checks are triggered in src/cartridges/int_adyen_webhooks/cartridge/notify.js, which serves as the entry point for all Adyen webhook notifications. This controller calls checkAuth.check(req) for Basic Authentication and handleHmacVerification() (which uses checkAuth.validateHmacSignature) for HMAC validation before executing the business logic in handleNotify.js to update order statuses.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →