What SharedSecret Actually Is

SharedSecret in cXML is, despite the technical-sounding name, just a string that both the buyer and supplier agree on in advance. It's essentially a password — but used differently than typical password authentication.

SharedSecret is not encrypted, not hashed at rest, not exchanged via key agreement. It's just a string that proves both parties know the same secret.

How It Appears in cXML

When a buyer's procurement system sends a PunchOutSetupRequest, the SharedSecret appears in the Sender credentials block:

<Sender>
  <Credential domain="NetworkID">
    <Identity>buyer_network_id</Identity>
    <SharedSecret>Y0urSh@redSecret123</SharedSecret>
  </Credential>
  <UserAgent>Oracle Fusion iProcurement</UserAgent>
</Sender>

The supplier's endpoint extracts this value and compares it against the stored SharedSecret for that buyer. If they match — authenticated. If not — rejected.

The Two Authentication Methods

Method 1 — Plain Comparison (Simplest)

The supplier simply compares the SharedSecret in the request against the one in their database:

if (received_secret === stored_secret_for_buyer) {
    authenticate_and_create_session();
} else {
    return_401_unauthorized();
}

This is the most common approach. Simple, straightforward, and good enough for most B2B implementations.

Method 2 — HMAC Signature (More Secure)

In stricter implementations, the SharedSecret is used as the key for HMAC-SHA256 signing. The buyer signs a portion of the request, and the supplier verifies the signature:

// Buyer side (signing)
$signature = hash_hmac('sha256', $payload, $shared_secret);

// Supplier side (verification)
$expected = hash_hmac('sha256', $payload, $shared_secret);
$valid = hash_equals($expected, $received_signature);

This protects against tampering — even if someone intercepts the request, they can't modify it without breaking the signature.

In practice, however, plain SharedSecret comparison is what most procurement systems use. HMAC signing is reserved for high-security implementations or specific buyer requirements.

The Security Model

cXML's security model relies on three layers:

  1. HTTPS (TLS 1.2+) — encrypts traffic in transit, prevents wire-tapping
  2. Per-buyer SharedSecret — isolates each buyer's identity
  3. BuyerCookie — session-level binding, prevents session hijacking

If any one of these layers is broken, the entire authentication chain fails. Suppliers must enforce all three.

Common Mistakes

Mistake 1 — Leading or Trailing Whitespace

The single most common cause of "Authentication Failed" errors:

Stored:     "Y0urSh@redSecret123"
Received:   " Y0urSh@redSecret123"  ← leading space!

The buyer's IT team might have pasted the secret with a stray space. The supplier's database might have stripped or kept whitespace differently. Result: comparison fails.

Fix: Always trim() both sides before comparing. Better yet, configure your input form to strip whitespace automatically.

Mistake 2 — Case Sensitivity

Stored:     "MySecret123"
Received:   "mysecret123"  ← lowercase!

cXML SharedSecret is case-sensitive. MySecret123 and mysecret123 are different secrets.

Fix: Never normalize case. Compare exact strings. If your buyer's system is normalizing — escalate to their IT.

Mistake 3 — Special Character Encoding

XML has reserved characters. If your SharedSecret contains &, <, >, ", or ', they must be XML-encoded:

Secret with ampersand:    "Pass&word"
XML-encoded:              "Pass&amp;word"

The buyer's system encodes when sending; the supplier's parser decodes when receiving. If either side fails to handle encoding correctly, secrets won't match.

Fix: Avoid special XML characters in SharedSecrets. Stick to alphanumeric + safe symbols: ! @ # $ % * - _ +

Mistake 4 — Storing the Wrong Field

cXML has multiple credential blocks: <From>, <To>, and <Sender>. SharedSecret only appears in <Sender>. Some implementations accidentally check <From> for the secret — which is wrong.

Fix: Always extract SharedSecret from the <Sender><Credential> block specifically.

Mistake 5 — Logging Secrets in Plaintext

Some logging implementations capture the full incoming cXML — including the SharedSecret — in audit logs or error logs.

Risk: If logs are exposed (developer access, backup files, accidental sharing), all your buyer secrets are compromised.

Fix: Always mask SharedSecret values in logs:

// Log this:
"SharedSecret": "Y0u***************123"

// Not this:
"SharedSecret": "Y0urSh@redSecret123"

Mistake 6 — Reusing Secrets Across Buyers

Using the same SharedSecret for multiple buyers breaks isolation. If one buyer's secret leaks, all buyers using the same secret are compromised.

Fix: Generate a unique SharedSecret for each buyer. Treat them as buyer-specific credentials.

How to Generate a Strong SharedSecret

A good SharedSecret should be:

One-liner to generate in PHP:

echo bin2hex(random_bytes(16)); 
// Example: a3f8d2b1e4c7f9a0e2d5c8b3f7a1e9d4

Or in JavaScript:

require('crypto').randomBytes(16).toString('hex');
// Example: 7b3a9c2e1d4f5a8b6c0d3e7f9a2b4c1d

Buyer-Side vs Supplier-Side Generation

Industry convention: the supplier generates the SharedSecret and provides it to the buyer.

Rationale:

However, some enterprise buyers have their own conventions — Oracle Fusion, for instance, sometimes assigns a Network ID and prefers the buyer's IT to generate the secret. Both approaches work as long as both parties agree.

Rotation and Lifecycle

How often should you rotate SharedSecrets?

TriggerAction
Suspected compromiseRotate immediately
Departure of buyer's IT staff who had accessRotate within 7 days
Annual security auditOptional rotation
End of buyer contractDisable buyer entirely
Normal operationNo rotation needed

Unlike user passwords, SharedSecrets don't have a recommended rotation schedule. They're machine credentials — rotate when there's a reason to.

The HMAC Signature Approach (Advanced)

For high-security implementations, some buyers require HMAC-SHA256 signing of cXML payloads. This goes beyond simple SharedSecret comparison.

How It Works

  1. Buyer constructs the cXML payload
  2. Buyer computes HMAC-SHA256(payload, shared_secret)
  3. Signature is added as a header or extrinsic field
  4. Supplier receives request, computes expected signature
  5. Supplier compares received vs expected using constant-time comparison

Why Use HMAC Instead of Plain Comparison

When You Need It

How PunchOutHub Handles This

PunchOutHub implements:

This gives suppliers compliance with both standard and high-security buyer requirements out of the box.

Quick Reference

AspectBest Practice
Length24+ characters
GenerationCryptographically random
Storage (your side)Encrypted at rest if possible
TransmissionHTTPS only, never email
LoggingAlways masked
Per buyerUnique secret each
ComparisonConstant-time, case-sensitive
RotationOn compromise or staff change
BackupStored in your password manager, not git