Problem/Motivation

CyberSource is deprecating HTTP_SIGNATURE messaging on the REST API by September 2026. After that date, requests using HTTP Signature will be rejected. The official documentation now flags HTTP Signature pages with [DEPRECATING]:

The replacement that requires the least operational change for existing merchants is JWT v2 signed with the existing shared secret key pair (HMAC HS256). This path is not deprecated and reuses the same credentials the module already collects
today:

The module currently hardcodes HTTP Signature in src/Plugin/Commerce/PaymentGateway/Flex.php:139:

$merchantConfig->setAuthenticationType('HTTP_SIGNATURE');

Note: Message-Level Encryption (MLE) is documented as optional and is not required by the September 2026 deadline. P12-based JWT (RS256) and MLE are intentionally out of scope for this issue and should be addressed in a follow-up alongside a major version bump.

Proposed resolution

Add a backward-compatible JWT v2 (HS256) authentication path to the cybersource_flex gateway, keeping the existing credential fields:

  1. Add an auth_type radio to the gateway form: http_signature (default, current behavior) or jwt_shared_secret. No new credential fields are needed; key_serial_number and key_shared_secret are reused.
  2. The official PHP SDK cybersource/rest-client-php only implements JWT signing with P12 + RS256, so when auth_type = jwt_shared_secret the module signs the JWT itself using firebase/php-jwt (already pulled as a transitive SDK
    dependency). The token is injected via a Guzzle middleware on the SDK's HTTP client, replacing the Authorization header the SDK would otherwise add.
  3. Build the JWT v2 payload per the spec: v-c-jwt-version: "2",
    v-c-merchant-id, jti (UUID v4), iat, exp = iat + 120, iss (merchant ID), request-method, request-host, request-resource-path, plus digest and
    digestAlgorithm: SHA-256 for POST/PUT/PATCH. Header: alg: HS256, kid: <key_serial_number>, typ: JWT. Sign with the base64-decoded shared secret.
  4. Extend config/schema/commerce_cybersource.schema.yml with the auth_type key, and add a hook_update_N in commerce_cybersource.install that sets auth_type = http_signature on existing gateway entities so update
    paths preserve current behavior.
  5. Reference: StandAloneJWTv2.php for the JWT v2 payload structure (the sample uses RS256 + P12; the construction logic is identical apart from the
    signing algorithm and key).

Remaining tasks

Add a backward-compatible JWT v2 (HS256) authentication path to the cybersource_flex gateway, keeping the existing credential fields:

  1. Add an auth_type radio to the gateway form: http_signature (default, current behavior) or jwt_shared_secret. No new credential fields are needed; key_serial_number and key_shared_secret are reused.
  2. The official PHP SDK cybersource/rest-client-php only implements JWT signing with P12 + RS256, so when auth_type = jwt_shared_secret the module signs the JWT itself using firebase/php-jwt (already pulled as a transitive SDK
    dependency). The token is injected via a Guzzle middleware on the SDK's HTTP client, replacing the Authorization header the SDK would otherwise add.
  3. Build the JWT v2 payload per the spec: v-c-jwt-version: "2",
    v-c-merchant-id, jti (UUID v4), iat, exp = iat + 120, iss (merchant ID), request-method, request-host, request-resource-path, plus digest and
    digestAlgorithm: SHA-256 for POST/PUT/PATCH. Header: alg: HS256, kid: <key_serial_number>, typ: JWT. Sign with the base64-decoded shared secret.
  4. Extend config/schema/commerce_cybersource.schema.yml with the auth_type key, and add a hook_update_N in commerce_cybersource.install that sets auth_type = http_signature on existing gateway entities so update
    paths preserve current behavior.
  5. Reference: StandAloneJWTv2.php for the JWT v2 payload structure (the sample uses RS256 + P12; the construction logic is identical apart from the
    signing algorithm and key).

User interface changes

API changes

None breaking. The gateway plugin gains additional configuration keys. The default value of auth_type is http_signature for new and existing gateways, so the public API surface and runtime behavior of unmigrated sites is unchanged.

Data model changes

A hook_update_N seeds auth_type = http_signature on existing gateway entities. No content entity or database schema changes.

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

plopesc created an issue. See original summary.

plopesc’s picture

Issue summary: View changes
plopesc’s picture

Issue summary: View changes

plopesc’s picture

Status: Active » Needs review

Moving to NR

jsacksick’s picture

@plopesc: We should be able to skip the update hook for setting the auth type right? If we ensure the default is the current signature method (i.e. http_signature)?

plopesc’s picture

Yes, we could skip it, assuming that the code will infer that http_signature if empty.

However, I think that might be worth to consolidate the data unless you have other concerns.

plopesc’s picture

Hello!

Is there anything we could do to try to push this one forward?

  • jsacksick committed bef8ec16 on 8.x-1.x authored by plopesc
    feat: #3587372 Add JWT v2 authentication support before September 2026...
jsacksick’s picture

Status: Needs review » Fixed

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

  • jsacksick committed bef8ec16 on 2.x authored by plopesc
    feat: #3587372 Add JWT v2 authentication support before September 2026...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.