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

> Secure your Adyen webhook endpoints on Salesforce Commerce Cloud with Basic Auth and HMAC signature verification. Learn how to validate incoming requests before processing data.

- Repository: [Adyen/adyen-salesforce-commerce-cloud](https://github.com/adyen/adyen-salesforce-commerce-cloud)
- Tags: how-to-guide
- Published: 2026-02-23

---

**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`](https://github.com/adyen/adyen-salesforce-commerce-cloud/blob/main/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`](https://github.com/adyen/adyen-salesforce-commerce-cloud/blob/main/checkNotificationAuth.js) (lines 26-31).

## Where Authentication Checks Are Triggered

The webhook controller [`notify.js`](https://github.com/adyen/adyen-salesforce-commerce-cloud/blob/main/notify.js) invokes the authentication helper functions before processing any notification data:

```javascript
// 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`](https://github.com/adyen/adyen-salesforce-commerce-cloud/blob/main/libAuthenticationUtils.js) decodes the `Authorization: Basic ...` header, splits the credentials on the colon delimiter, and compares them with the stored site preferences:

```javascript
// 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:

```javascript
// 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`](https://github.com/adyen/adyen-salesforce-commerce-cloud/blob/main/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:

```javascript
// 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:

```javascript
// 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:

```javascript
// __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`](https://github.com/adyen/adyen-salesforce-commerce-cloud/blob/main/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`](https://github.com/adyen/adyen-salesforce-commerce-cloud/blob/main/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`](https://github.com/adyen/adyen-salesforce-commerce-cloud/blob/main/src/cartridges/int_adyen_webhooks/cartridge/libs/libAuthenticationUtils.js) | Utility library containing `checkGivenCredentials` and `calculateHmacSignature` functions. |
| [`src/cartridges/int_adyen_webhooks/cartridge/handleNotify.js`](https://github.com/adyen/adyen-salesforce-commerce-cloud/blob/main/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`](https://github.com/adyen/adyen-salesforce-commerce-cloud/blob/main/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`](https://github.com/adyen/adyen-salesforce-commerce-cloud/blob/main/checkNotificationAuth.js) module exposes `check()` for Basic Auth and `validateHmacSignature()` for HMAC validation, both used by [`notify.js`](https://github.com/adyen/adyen-salesforce-commerce-cloud/blob/main/notify.js) before processing any notifications.
- Failed authentication attempts return **HTTP 403 Forbidden**, preventing unauthorized access to order management logic in [`handleNotify.js`](https://github.com/adyen/adyen-salesforce-commerce-cloud/blob/main/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`](https://github.com/adyen/adyen-salesforce-commerce-cloud/blob/main/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`](https://github.com/adyen/adyen-salesforce-commerce-cloud/blob/main/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`](https://github.com/adyen/adyen-salesforce-commerce-cloud/blob/main/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`](https://github.com/adyen/adyen-salesforce-commerce-cloud/blob/main/handleNotify.js) to update order statuses.