Manav.id
Compliance · 4 min read

Transaction confirmation in the browser: the extension that never shipped

Transaction confirmation in the browser: the extension that never shipped

The gap is well known to everyone who works on the specification: the browser can prove a user touched their authenticator, and cannot prove what was on screen when they did.

Why has transaction confirmation never shipped in browsers?

Because no single party owns it. The extension would have the authenticator display the transaction on its own screen and include that text in the signature. Most authenticators have no screen, text and internationalisation are hard on constrained hardware, and the feature spans vendors, browsers and operating systems.

Key takeaways
  • Transaction confirmation extensions would have the authenticator display text and include it in the signature. They are not implemented in practice.
  • The obstacles are structural — constrained displays, internationalisation, platform fragmentation — rather than a lack of interest.
  • The available approximation binds the challenge to a digest of the rendered statement, which is weaker in a specific, nameable way.

What the extension would do

Transaction confirmationAuthenticator displays the textText is inside the signatureHost cannot alter the displayNot deployableDigest bindingHost displays the statementDigest is the challengeHost could display falselyDeployable todayvs

The relying party supplies a short text describing the transaction. The authenticator displays that text on its own screen, requires confirmation, and includes the text in the signed data.

The effect is that the signature attests to what was displayed, on hardware the host cannot control. That closes the trusted-display gap properly, which nothing else does.

Why it has not shipped

ObstacleDetail
Display capabilityMost authenticators have no screen; those that do are small and monochrome
Text lengthA useful description of a payment does not fit in a line
InternationalisationRendering arbitrary scripts on constrained hardware is hard
Platform mediationPlatform authenticators would need OS-level display support
FragmentationSome authenticators support it and most do not, so a relying party cannot depend on it
IncentivesNo party owns the problem; it spans authenticator vendors, browsers and platforms

The last row is the real one. The specification work is tractable; coordinating vendors, browser engines and operating systems around a feature none of them owns has not happened in ten years.

What can be done with current primitives

The challenge in a WebAuthn ceremony is arbitrary bytes chosen by the relying party. Nothing prevents making those bytes a digest of a rendered statement.

statement = {
  "action": "payment.release",
  "beneficiary": "Acme Supplies Ltd",
  "account": "****9023",
  "amount": "84,000.00",
  "currency": "GBP"
}
challenge = sha256(JCS(statement))

# Displayed to the user, rendered FROM the statement
# Signed: the challenge, plus origin and rpIdHash by the browser
# Verified: signature valid, challenge == sha256(JCS(statement)),
#           statement matches the payload about to execute

The result is a signature cryptographically bound to a specific statement, verifiable by a third party, with the statement human-readable.

Where the approximation is weaker

Precisely one place, and it should be stated plainly rather than glossed over.

The rendering happens on the host. If the host's display path is compromised, the user can be shown one statement while a different one is signed. The authenticator has no view of the screen and cannot detect this.

ThreatExtensionDigest binding
Compromised host alters the payload after displayPreventedPrevented — digest mismatch
Compromised host displays false textPreventedNot prevented
Relying party verification bugNeither preventsNeither prevents
Phishing proxyPrevented by origin bindingPrevented by origin binding

The second row is the entire difference. Everything else is equivalent, which is why the approximation is worth deploying rather than waiting.

Reducing the residual risk

  1. Render on a different device. A transaction initiated on a laptop, displayed and confirmed on a phone, requires two compromises rather than one.
  2. Verify independently before execution. A service that recomputes the digest from the payload it is about to execute catches manipulation between display and execution.
  3. Use a secondary channel. A message to the user restating what was authorised, after the fact, surfaces a discrepancy quickly — which does not prevent it but bounds the damage.
  4. Keep statements short and specific. A user scanning a long statement notices less; four fields are read, twenty are not.

What to expect

Honest assessment: transaction confirmation in the browser is unlikely to become broadly deployable soon. The obstacles are organisational rather than technical, and organisational obstacles of this shape persist.

Building on digest binding now is therefore not a stopgap for a thing arriving next year. It is the available mechanism, and a system built on it would need minimal change if the extension ever shipped — the statement is already canonical and already rendered from itself.

Where the approximation is weaker, precisely

Threat by threat
ThreatExtensionDigest binding
Payload altered after displayPreventedPrevented
Host displays false textPreventedNot prevented
Relying party verification bugNeitherNeither
Phishing proxyOrigin bindingOrigin binding

One row differs. Everything else is equivalent, which is why the approximation is worth deploying rather than waiting for a feature that has not moved in a decade.

Objections and honest limits

“So we should wait.” The obstacles are organisational rather than technical, and organisational obstacles of this shape persist. A system built on digest binding needs minimal change if the extension ever ships.

“Rendering on a second device solves it fully.” It requires two compromises rather than one, which is a real improvement and not a proof. Combined with independent verification before execution it is the practical ceiling today.

Reducing the residual display risk

  1. Render on a different device from the one that built the transaction. Two compromises, not one.
  2. Verify independently before execution. Catches manipulation between display and execution.
  3. Notify out of band after the fact. Bounds the damage rather than preventing it.
  4. Keep statements short. Four fields are read; twenty are not.

Terms used here

Transaction confirmation
A proposed extension having the authenticator display and sign the transaction text.
Digest binding
Using a hash of the rendered statement as the WebAuthn challenge, so the signature covers a specific statement.
Trusted display
Hardware the host cannot control, which is what the extension would provide.

Frequently asked questions

What would transaction confirmation add? The authenticator would display the text on its own screen and include it in the signature, so the attestation covers what was displayed on hardware the host cannot control.

Why hasn't it shipped? Most authenticators have no display, text and internationalisation are hard on constrained hardware, and no single party owns a feature spanning vendors, browsers and platforms.

How is digest binding weaker? In exactly one way: a compromised host can display false text. It still prevents payload alteration after display, and origin binding still defeats phishing proxies.

How do you reduce the residual risk? Render on a different device from the one that built the transaction, verify the digest independently before execution, and keep statements short enough to be read.

What would the extension add? The authenticator would display the text on hardware the host cannot control and include it in the signature.

Where this fits in Manav

Manav's receipt is deliberately small: RFC 8785 canonicalisation, one algorithm, keys at a well-known URL, and a verifier short enough to audit or reimplement.

Read the architecture →

Sources and further reading