Why Authentication Isn't Enough
The "Who" vs The "What"
This article is part of a series on understanding the hows and whys of JSON Web Signatures (JWS).
There's accompanying code: it's refered to and linked throughout the content. But if you'd rather just read raw code, head over here.
When an issuer claims they never approved that €50'000 authorization, can you prove they did?
Here's a problem that keeps payment scheme engineers up at night: proving what actually happened in authorization and capture flows. You've got authentication sorted with OAuth 2.0 or mTLS, and your API security looks solid. Your logs show the issuer connected, their credentials were valid, and the request came from their known IP range. Case closed, right?
Wrong.
The Trust Gap
Picture this: It's Monday morning, and you get an urgent call from your Head of Risk. A major issuing bank is disputing an authorization approval from Friday afternoon: a €50'000 transaction at a luxury retailer. Their fraud team claims they never approved it. They're suggesting your payment scheme's authorization API was compromised, or worse, that you fabricated the approval to push through a fraudulent transaction.
You pull up your logs. The authorization approval message came in with valid OAuth credentials. The mTLS handshake completed successfully. The source IP matches their known network range. Everything checked out, so you processed the authorization and the merchant released goods. But now they're demanding proof of exactly what they sent you, and your logs just show “authorization approval received from issuer.”
Welcome to the gap between authentication and non-repudiation.
Authentication tells you WHO sent the message. Your OAuth tokens prove the issuer's identity. Your mTLS certificates prove the connection came from their authorization platform. All good.
But authentication doesn't prove WHAT they approved. The issuer could claim they approved €5'000, not €50'000. They could argue the merchant category code was different. They could insist they sent a decline, not an approval.
Without cryptographic proof of the exact payload exchanged between scheme participants, you're stuck in “he said, she said” territory. And in the world of payment schemes, that's where chargebacks, liability disputes, and scheme rule violations live.
The Business Cost of “He Said, She Said”
This pattern repeats across production payment scheme operations.
Disputed authorization approvals
Payment schemes process authorization approvals that appear legitimate. Months later during dispute resolution, issuers claim they never approved specific transactions. Authentication logs show API calls with valid credentials, but can't prove the exact approval message parameters. Outcome: Scheme liability shifts incorrectly, €100'000+ chargebacks absorbed by the wrong party, and protracted arbitration. This scenario plays out consistently in schemes relying solely on authentication logs.
Authorization release disputes
Acquirers claim they instructed authorization releases (to free up cardholder funds), but issuers claim they never received those instructions. Without cryptographic proof of the release message, determining who's liable for customer complaints about held funds becomes impossible. The scheme typically absorbs dispute resolution costs and relationship damage with both parties. Conversely, when an acquirer submits a capture that gets declined due to the referenced authorization having been released, the scheme must be able to demonstrate that the release instruction originated from the acquirer.
Scheme rule compliance audits
Scheme auditors demand proof that authorization messages came from authenticated participants and weren't tampered with. Schemes produce authentication logs but lack cryptographic proof of message integrity.
Across these scenarios, the limitation is consistent: Authentication proves participant identity, but doesn't prove message content or integrity.
Authentication vs Authorization vs Non-Repudiation
Let's get precise about what we're actually trying to achieve:
Authentication: “You are who you say you are”
- Answers: "Is this really Issuer XYZ connecting?"
- Proves identity of the scheme participant
- OAuth 2.0 access tokens
- mTLS client certificates
- API keys
Authorization: “You're allowed to do this”
- Answers: "Is this issuer allowed to approve authorizations for the given end user?"
- Proves the participant has permission
- Role-based access control (RBAC)
- Scopes and permissions
- Policy engines
Non-Repudiation: “You can't deny you did this” ← The missing piece
- Answers: "Did issuer XYZ actually approve this €50'000 authorization with these specific parameters?"
- Proves the participant actually sent this specific message
- Digital signatures on payloads
- Cryptographic binding of data to identity
- Audit trails that survive disputes
Most APIs stop at authentication and authorization. They prove who you are and what you're allowed to do. But they can't prove what you actually did.
This is where JSON Web Signature (JWS) comes in.
Enter JSON Web Signature (JWS)
JWS uses asymmetric cryptography (or symmetric, but we'll get to that in a later post in this series) to create an unforgeable link between a payload and the signer's private key. Here's how it works, conceptually:
- The signer has a private key (kept secret) and publishes a corresponding public key
- To sign an authorization message, they use their private key to create a digital signature over the exact bytes of the JSON payload
- Anyone with the public key can verify that this specific payload was signed by the holder of the corresponding private key
- The signature is mathematically bound to the payload: change even one character and verification fails
The beauty is in the math of asymmetric cryptography: creating a valid signature requires the private key, but anyone can verify it with just the public key. And critically: you can't create a valid signature for different data without the private key.
Here's what makes this powerful:
Integrity protection: If anyone modifies the authorization amount, merchant category code, or any other field after signing, the signature verification fails. You instantly know the data was tampered with. This comes in handy for dynamically linking SCA to the payment for PSD2 compliance if JWS is used for user-facing transaction initiation.
Non-repudiation: The signature can only be created by someone with access to the private key. The issuer can't claim someone else sent the approval, because only they have that private key.
Audit trail: Store the original signed payload, and you can re-verify it years later (see how). The cryptographic proof doesn't expire or degrade.
This isn't just best practice: in many jurisdictions and industries, it's mandatory.
Modern Payment Scheme Implementations
The benefits of implementing JWS signatures spans all financial message types:
- Authorization approval/decline messages from issuers
- Authorization release instructions from acquirers
- Settlement confirmation messages
- Dispute initiation and response messages
This approach solves multiple problems across the scheme:
- Fraud prevention: Even if an attacker compromises an acquirer network, they can't modify authorization amounts without the acquirer's private key
- Dispute resolution: When an issuer claims "we declined that transaction," the scheme can prove they approved it with cryptographic certainty
- Regulatory compliance: Demonstrates strong controls over message integrity for payment system oversight
Why Logs Aren't Enough for Regulators
You might be thinking: “We already log everything. Our logs show who sent what and when. Isn't that enough?”
Regulators and auditors don't accept logs as proof for a simple reason: logs can be modified, forged, or selectively deleted. Even with append-only logging systems and tamper-evident data stores, there's always a question of “did the operator modify this?”
Cryptographic signatures are different since the proof is in the pudding, and the math is independently verifiable. As we'll see, an auditor can:
- Take your stored JWS
- Fetch the issuer's or acquirer's public key
- Verify the signatures independently
- Be certain that this exact payload was signed by that specific participant
No trust in your scheme's systems required: the math either works out or it doesn't.
The Authentication vs. Non-Repudiation Matrix
Let's make this concrete with a decision matrix for different scenarios:
| Scenario | Authentication Only | Authentication + JWS |
|---|---|---|
| Issuer disputes authorization amount | “Our logs show €50'000 approved” vs. “We approved €5'000” – no way to prove | Signature over exact approval message proves amount was €50'000 |
| Attacker modifies authorization in transit | Modified authorization processed, no detection | Signature verification fails, attack detected immediately |
| Scheme rule audit | Logs can be questioned, may not satisfy auditors | Cryptographic proof satisfies card network oversight requirements |
| Issuer API key compromise | All approvals look legitimate | Can identify exact timeframe of fraudulent approvals by signature timestamps |
| Internal fraud investigation | Difficult to prove scheme staff didn't modify data | Signature proves authorization came from issuer, not internal manipulation |
| Chargeback dispute | Logs may not hold up in arbitration | Cryptographic signatures provide clear evidence for liability determination |
When Do You Actually Need Non-Repudiation?
Not every API needs JWS. If you're building an internal microservice that reads product inventory, authentication and authorization are probably sufficient. But you absolutely need non-repudiation for:
Payment scheme operations
- Authorization approvals and declines
- Authorization releases and reversals
- Settlement instructions
- Chargeback and dispute messages
Multi-party payment systems
- Card network processing platforms
- ACH/clearing house operations
- Real-time payment schemes
- Any system where liability must be definitively assigned
High-value or high-risk transactions
- Transactions above scheme risk thresholds
- Cross-border authorizations
- Merchant category codes prone to disputes
- Systems where disputes could shift six-figure liabilities between participants
Long-term audit requirements
- Industries with 7+ year data retention requirements
- Systems that might face legal discovery years after the fact
- Compliance frameworks requiring tamper-evident audit trails
But you might not need it for:
- Read-only APIs
- Internal microservices within a trusted boundary
- Low-stakes operations where disputes are unlikely or easily resolved
What JWS Doesn't Solve
Let's be clear about the boundaries. JWS provides non-repudiation, but it's not a silver bullet:
JWS doesn't prevent key compromise. If an attacker steals an issuer's or acquirer's private key, they can create valid signatures. You'll need additional monitoring, key rotation procedures,, and incident response plans.
JWS doesn't prove the authorization was legitimate. It just proves someone with access to the private key signed it. Whether that key holder had actual authority within the issuer's risk systems is a separate question (though it shifts liability to the participant for their internal key management).
JWS doesn't encrypt payloads. Signatures prove integrity and authenticity, but the payload is still readable. If you need confidentiality, you want JWE (JSON Web Encryption) in addition to JWS.
JWS doesn't automatically make your system secure. You can implement JWS badly (weak algorithms, missing validation, poor key management) and still have vulnerabilities. The next posts in this series will help you avoid those pitfalls.
The Path Forward
Now that you understand why authentication isn't enough and how JWS provides cryptographic non-repudiation, let's implement JWS signing and verification in Elixir using the JOSE library. You'll learn about wire formats (compact vs. flattened JSON serialization), how to handle gotchas like clock skew and white space, and how to build a Phoenix Plug for production APIs.
This article is part of a series on understanding the hows and whys of JSON Web Signatures (JWS).
There's accompanying code: it's refered to and linked throughout the content. But if you'd rather just read raw code, head over here.