This article explores the architectural challenges of securely storing private keys for DPoP (Demonstrating Proof-of-Possession) in browser-based applications, a critical component for preventing OAuth token replay attacks. It highlights the "storage paradox" where browser mechanisms like IndexedDB, while preventing key extraction, still allow XSS attacks to use the key as a signing oracle. The discussion then focuses on the Backend-for-Frontend (BFF) pattern as the industry standard solution for shifting key management server-side, outlining its benefits and trade-offs.
Read original on InfoQ CloudDPoP (Demonstrating Proof-of-Possession) is a critical security mechanism in OAuth 2.0, designed to prevent token replay attacks by binding access tokens to a client's cryptographic key pair. While effective, its implementation in browser-based Single Page Applications (SPAs) faces a significant architectural paradox: how to store the private signing key securely while making it usable by client-side code without exposing it to malicious scripts (e.g., via XSS).
The Web Crypto API allows storing non-extractable CryptoKey objects in IndexedDB. This prevents direct export of the private key bytes. However, this does not prevent an attacker who achieves XSS from using the browser's crypto subsystem as a "signing oracle". The attacker can invoke `crypto.subtle.sign()` with the stored key to generate valid DPoP proofs for arbitrary requests, even without ever extracting the raw key material. This loophole renders the non-extractability insufficient against script-level attacks.
await crypto.subtle.exportKey('jwk', privateKey); // throws InvalidAccessError
await crypto.subtle.sign(alg, privateKey, payload); // succeeds — valid signatureThe Oracle Attack
An XSS attacker can use the browser's crypto subsystem as a signing oracle. They can retrieve the non-extractable CryptoKey handle from IndexedDB and call `crypto.subtle.sign()` to produce valid DPoP proofs for arbitrary requests, effectively bypassing the token binding security without ever extracting the private key.
To address the DPoP storage paradox, the industry has largely converged on the Backend-for-Frontend (BFF) pattern. This shifts the trust boundary and key management responsibility from the insecure browser environment to a server-side component. The BFF acts as a confidential OAuth client, handling the DPoP key generation, authorization code exchange, token management, and DPoP proof generation for outbound API requests. The browser only receives an HTTP-only, Secure, SameSite cookie to maintain a session with the BFF, never directly touching sensitive key material or tokens.
While the BFF pattern introduces additional infrastructure overhead and complexity (e.g., requiring a dedicated server for each frontend type), it is considered the most secure industry standard for browser-based sender-constrained tokens, effectively mitigating the DPoP storage paradox and the oracle attack.