← Back to list

Sender-Constrained Web Security: From DPoP-Bound Sessions to End-to-End Authorization

Introduction

Declerus Yves Kerbens · 2025-07-30 04:52 · 12 claps · 17.6 min read
#oidc #dpop #oauth #access-token #fapi
Open on Medium ↗
Wiki topics: FT · Fine-tuning & Adaptation LIT · Literature & Writing

Sender-Constrained Web Security: From DPoP-Bound Sessions to End-to-End Authorization

Introduction

In Part 1 of this series, we laid the foundation for a truly secure session by solving the problem of bearer token replay. We detailed a process to transform the common session cookie into a sender-constrained credential, cryptographically binding it to a non extractable key securely generated in the browser. This ensures that even if a session cookie is stolen, it is useless without the user’s private key.

With a secure session established, we now turn to the primary purpose of that session: authorizing access to protected data. This presents a new and crucial challenge: if the access token is held securely on the server (as per the BFF best practice), how can the browser generate the DPoP proof — which requires a hash of that very token — for every API call?

In this article, we will analyze and present a dual-mode architecture that allows the browser to securely obtain the necessary information to construct valid proofs without ever accessing the token itself. This model takes full advantage of both worlds: the security of server-side token management and the integrity of browser-side cryptographic proof-of-possession.

The Unbroken Chain of Possession

The entire security model for authorizing resource access rests on a single, foundational principle established in Part 1: the Relying Party (RP) must use a trusted public key for the browser. This trust is not assumed; it is explicitly bootstrapped through the OIDC authentication flow, where the OpenID Provider (OP) or the Authentication Server acts as the trusted third party that vouches for the link between the authenticated user and their specific browser key.

With this trusted key as our anchor, we can now analyze why end-to-end proof-of-possession is critical. To understand why end-to-end proof-of-possession is critical, we must look at the core security mechanism of DPoP as defined in RFC 9449. The standard was designed to defeat token replay attacks by ensuring that a client presenting a token also possesses the specific cryptographic key to which that token is bound. This is achieved through two complementary bindings:

  1. The Token-to-Key Binding: A DPoP-bound access token contains a cnf (confirmation) claim with a jkt (JWK thumbprint). This cryptographically asserts: "This token may only be used by the holder of the private key matching this thumbprint."
  2. The Proof-to-Token Binding: On every API call, the client sends a DPoP proof. This proof contains several claims, but the most important for this context is the ath (access token hash). This claim asserts: "I am using my private key to authorize a request for this specific access token."

When a Resource Server receives a request, it validates this entire chain. It checks that the key used to sign the DPoP proof matches the jkt in the access token, and that the ath in the proof matches the hash of the access token itself.

The Gap in a Traditional BFF Implementation

In a standard Backend-for-Frontend (BFF) architecture, the DPoP security model is often only partially implemented, creating a critical vulnerability.

A common but flawed approach is for the BFF to manage the DPoP key pair on behalf of the browser. This results in a scenario where the DPoP-bound access token and the private key used to sign proofs for it reside on the same shared server.

Browser --- (Bearer Session Cookie) ---> BFF (Holds Token + DPoP Key) --- (DPoP Proof) ---> Resource Server

This colocation of secrets completely negates the security promise of DPoP. If a malicious actor gains access to the server — either through an external attack or as a malicious insider — they can steal both the token and the key. With both components in hand, they can generate valid DPoP proofs for the stolen token and access protected resources at will.

A simple challenge-response from the Resource Server to the BFF does not mitigate this, as any attacker in control of the BFF can also intercept and solve the challenge. The chain of possession is fundamentally broken.

The Motivation for an End-to-End Model

True security requires an unbroken cryptographic chain from the original sender to the final recipient. The DPoP proof must be generated by the ultimate client — the browser — and be forwarded, unmodified, through the BFF to the Resource Server.

While this level of security may seem irrelevant for some applications, the paradigm is different for high-stakes applications such as finance, identity, and privacy-sensitive systems, where the end-user must always be provably in control.

Additionally, this end-to-end model can be extended to guarantee the integrity of the transaction itself. For example, consider a user drafting a financial transaction. By including a hash of the transaction payload as a custom claim within the browser-generated DPoP proof, this mechanism allows the user to:

  1. Compute the integrity of their data at the source (the browser).
  2. Provide a non-repudiable link between their key, the request, and the exact data being sent.

The server can then validate this payload hash upon receipt. Any modification to the transaction data by an intermediary — even a compromised BFF — would invalidate the DPoP proof, causing the request to be rejected. This architecture, therefore, evolves to provide not just sender-constrained access, but also sender-guaranteed transaction integrity, fulfilling the full security promise of RFC 9449.

Foundational about OIDC Tokens

Understanding OIDC Tokens

To fully appreciate the security model, it’s helpful to understand the roles of the three primary tokens used in a standard OpenID Connect (OIDC) flow. Each token has a distinct purpose and audience.

1. ID Token

  • Purpose: To prove that a user has been authenticated. It contains information (claims) about the user, such as their unique identifier (sub), name, and email, as well as metadata about the authentication event itself (e.g., when it happened).
  • Audience: The Relying Party (RP) or client application. The ID Token is meant for the RP to consume so it can understand who the user is without needing to call a separate user info endpoint.
  • Format: Always a JSON Web Token (JWT). It is signed by the Authorization Server (AS) so the RP can verify its authenticity and integrity.

Example (Decoded Payload):

{
  "iss": "<https://as.example.com>",
  "sub": "248289761001",
  "aud": "s6BhdRkqt3",
  "exp": 1311281970,
  "iat": 1311280970,
  "name": "Jane Doe",
  "email": "jane.doe@example.com",
  "auth_time": 1311280969
}

2. Access Token

  • Purpose: To authorize access to protected resources. It represents the permission (scope) granted to the client to act on behalf of the user.
  • Audience: The Resource Server (RS). The client application (or BFF) receives the Access Token and includes it in requests to the API (the RS). The RS validates the token to decide whether to grant access to the requested data.
  • Format: Can be an opaque string or a JWT. Modern systems often use JWTs so the RS can validate them locally without calling the AS. In our architecture, this is the token that is DPoP-bound.

Example (Decoded Payload of a JWT Access Token):

{
  "iss": "<https://as.example.com>",
  "sub": "248289761001",
  "aud": "<https://api.example.com/data>",
  "exp": 1311281970,
  "iat": 1311280970,
  "scope": "read:data write:data",
  "client_id": "s6BhdRkqt3",
  "cnf": {
    "jkt": "0ZcOCORZ888..."
  }
}

3. Refresh Token

  • Purpose: To obtain new ID and Access Tokens without requiring the user to log in again. Access Tokens are typically short-lived for security reasons; when one expires, the client can use a long-lived Refresh Token to get a new one.
  • Audience: The Authorization Server’s token endpoint. The Refresh Token is a secret credential meant only for the AS. It is highly sensitive and, in a BFF architecture, must never be exposed to the browser.
  • Format: Typically an opaque, high-entropy string whose format is only understood by the AS.

Example (Opaque String): 7x00a8S2–42MA-S234d-1245sS_12a3–234s

Understanding this separation of concerns is key. The ID Token handles authentication (who the user is), while the Access Token handles authorization (what the user can do). Our architecture leverages this standard model to add a layer of sender-constrained proof-of-possession on top.

Understanding DPoP-Bound Tokens

Standard OIDC tokens are like a physical key to a house: whoever holds the key can open the door. These are called “bearer” tokens. If the key is stolen, the thief can use it. Demonstrating Proof-of-Possession (DPoP), as defined in RFC 9449, upgrades this system. It transforms the token into a key that only works when used by the legitimate owner, much like a key that also requires a matching fingerprint scan.

This is achieved by combining two components: a DPoP proof (the fingerprint scan) and a DPoP-bound token (the lock that requires the fingerprint).

1. The DPoP Proof: A “Fingerprint” for Every Request

On every API call, the client must generate a DPoP proof. Think of this as a unique, one-time-use digital fingerprint scan that proves two things: the client possesses the secret private key, and it’s authorizing this exact request. This proof is a short-lived, signed JWT sent in the DPoP HTTP header.

Key Claims and Their Purpose:

  • jti (JWT ID): A unique "scan ID" that prevents an attacker from capturing and replaying this proof.
  • htm (HTTP Method): The method of the request (e.g., "GET"). This ensures the proof can't be reused for a "DELETE" request.
  • htu (HTTP URI): The full URL being requested. This locks the proof to a specific endpoint.
  • iat (Issued At): A timestamp to ensure the proof is recent.
  • ath (Access Token Hash): The hash of the access token being used. This cryptographically binds the proof to a specific token, ensuring this "fingerprint" can't be used with a different "key."

Example (Decoded DPoP Proof Payload):

{
  "jti": "-B7-4J-L4kL4Yp-42T-42p-L4M",
  "htm": "POST",
  "htu": "<https://api.example.com/data>",
  "iat": 1562262616,
  "ath": "fUHyO2r2Z3DZ53EsNrWBb0xWXoaNy59IiKCAqksmQEo"
}

This proof is then signed by the client’s private key.

2. The DPoP-Bound Token: The “Biometric Lock”

An access token becomes “DPoP-bound” when the Authorization Server embeds a cnf (confirmation) claim inside it. This claim acts as the biometric lock, storing a reference to the only fingerprint that is allowed to use this token.

  • cnf (Confirmation): A JSON object containing the key confirmation data.
  • jkt (JWK Thumbprint): The thumbprint (a unique, standardized hash) of the client's public key. This is the reference "fingerprint" that the lock is configured to check against.

Example (Decoded Payload of a DPoP-Bound Access Token):

{
  "iss": "<https://as.example.com>",
  "sub": "248289761001",
  "aud": "<https://api.example.com/data>",
  "exp": 1626003600,
  "iat": 1626000000,
  "scope": "read:data",
  "cnf": {
    "jkt": "0ZcOCORZ8887XmnGG_z61gw5O99d_32gGfX_a212c8I"
  }
}

The Validation: Checking the Key and the Fingerprint

When a Resource Server receives a request, it performs a rigorous, multi-step validation to ensure the sender is legitimate. The client sends both the DPoP-bound access token and the DPoP proof in the HTTP headers.

Example HTTP Request:

POST /data HTTP/1.1
Host: api.example.com
Authorization: DPoP eyJhbGciOiJSUzI1...
DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2Ii...
Content-Type: application/json
{ "some_data": "value" }

The server then performs the following checks:

  1. Is the Proof Valid? The server first checks the DPoP proof itself (from the DPoP header). It verifies the signature on the proof and ensures the htm and htu claims match the incoming request. This confirms the "fingerprint scan" is authentic and for this specific action.
  2. Does the Fingerprint Match the Lock? The server then checks if the proof belongs to the token.
  • It calculates the thumbprint of the public key from the DPoP proof.
  • It verifies that this thumbprint matches the jkt value in the access token's cnf claim.

3. Is it the Right Key for this Fingerprint? Finally, the server confirms the proof was created for this specific token.

  • It calculates the hash of the received access token (from the Authorization header).
  • It verifies that this hash matches the ath value in the DPoP proof.

If all these checks pass, the Resource Server has cryptographic proof that the request was made by the client who legitimately holds both the token and the private key it is bound to. A stolen token is now useless, as the thief cannot generate the required “fingerprint” for each new request.

Extending Proof-of-Possession Into the Authorization Flow

The Architectural Imperative: Browser-Side Proofs

The security of DPoP hinges on a non-negotiable requirement from RFC 9449: the Resource Server must validate that the access token’s jkt claim matches the key that signed the DPoP proof, and that the proof's ath claim matches the hash of the token itself.

This creates an architectural dilemma for a pure Backend-for-Frontend (BFF) pattern:

  1. If the BFF generates the DPoP proof, it signs on behalf of the user, breaking the end-to-end chain of possession. The Resource Server can only verify the BFF, not the original browser.
  2. If the BFF sends the access token to the browser to generate the proof, it violates the core security principle of the BFF pattern, which is to keep all tokens off the browser.

To achieve true end-to-end security, the architecture must enforce a strict separation of concerns, which we accomplish through a dual-channel design.

The Dual-Channel Solution: Separating the Token from the Proof

To resolve the dilemma, we split the authorization process into two distinct, coordinated channels, ensuring no single component holds both the token and the key.

  • The Confidential Token Channel (Server-Side): The BFF acts as the sole custodian of the access_token. Its responsibilities are to manage the token lifecycle with the Authorization Server and to compute the ath (access token hash) on demand. It never exposes the raw access token to the browser.
  • The Proof-of-Possession Channel (Browser-Side): The browser is the sole custodian of the private DPoP key. Its responsibility is to generate a fresh, signed DPoP proof for every single API request and token renewal operation, using the ath provided by the BFF.

This strict separation is the cornerstone of the architecture. A compromised server yields a token that is useless without the browser’s key. Conversely, a compromised browser (e.g., via a sophisticated XSS attack) can forge requests for the duration of the active session, but it cannot exfiltrate long-lived credentials that might exist on the server, preventing persistent, offline access.

Securing the Full Session Lifecycle

This architecture extends beyond single API calls to protect the entire session, from replay attacks to token renewal. We achieve this by securing two critical operations: individual API requests and the token renewal process itself.

1. Protecting Every Request: Replay Prevention

To secure individual API calls, every DPoP proof is uniquely bound to a specific request. As per the standard, this is enforced through several claims:

  • The htm (HTTP method) and htu (HTTP URI) claims lock the proof to a single action and endpoint.
  • The jti (JWT ID) and iat (issued at) claims ensure the proof is unique and recent, preventing it from being captured and replayed later.

For an even stronger guarantee, a Resource Server can issue a DPoP-Nonce in a 401 response. The BFF forwards this nonce to the browser, which must include it in its next DPoP proof, proving the proof was generated in direct response to a server challenge.

2. Securing Token Renewal: The DPoP-Bound Session as a Credential

In a traditional OAuth flow, a long-lived refresh_token is used to obtain new access tokens. However, as industry guidance from authorities like Auth0 confirms, a secure, HttpOnly session cookie is a perfectly valid mechanism for managing token renewal within a BFF pattern.

Our architecture adopts this principle and enhances it significantly. While a standard session cookie is a bearer credential, our DPoP-bound session cookie is a sender-constrained credential. Using it to authorize a token renewal is not just a valid alternative but a superior security pattern, as it requires active proof-of-possession from the browser — a guarantee a simple refresh token cannot provide.

The renewal process is therefore explicitly authorized by the active user session:

  1. When the BFF determines the access_token is expired, it requires the browser to prove it is still the legitimate owner of the session before it will request a new one.
  2. The browser initiates a renewal request to the BFF, authenticated with its DPoP-bound session cookie and, crucially, a new DPoP proof generated by the browser specifically for this renewal action.
  3. Only after the BFF validates this proof can it proceed to the Authorization Server to obtain a new access token. An attacker who has stolen the session cookie cannot forge this proof and therefore cannot extend their access.

This comprehensive approach ensures that initial authentication, every API call, and the token renewal process are all cryptographically bound to the user’s device. This aligns directly with the stringent requirements of high-assurance standards like FAPI 2.0, which mandates DPoP for sender-constraining tokens.

Putting It All Together: An End-to-End Request Flow

To see how these components provide end-to-end security, let’s walk through a complete API request, from the browser’s initial action to the Resource Server’s final validation.

Scenario: The browser needs to make a POST request to transfer funds. It has an active, DPoP-bound session cookie with the Backend-for-Frontend (BFF).

The Cryptographic Artifacts

Before we begin the flow, let’s define the key cryptographic elements that will be used throughout this example.

1. The Browser’s Key Pair The browser generates a key pair. The private key never leaves the browser. The public key’s thumbprint (jkt) is used to bind the access token.

  • Public Key (in JWK format):
{
  "kty": "EC",
  "crv": "P-256",
  "x": "l8_224e-hNudx0j-gX-jTj_i-n2y-gX-jTj_i-n2y-gX",
  "y": "gX-jTj_i-n2y-gX-jTj_i-n2y-gX-jTj_i-n2y-gX-jT",
  "alg": "ES256",
  "key_ops": ["verify"]
}
  • JWK Thumbprint (jkt) derived from this public key: N1hZ_-T4A48..._8I

2. The DPoP-Bound Access Token After the OIDC flow (detailed in Part 1), the Authorization Server issues this access token to the BFF. Crucially, because the authentication request included the browser’s key thumbprint, the AS embeds that same thumbprint in the token’s cnf.jkt claim. The BFF then holds this token securely. Notice the cnf.jkt claim matches the thumbprint of the browser's public key.

  • Access Token (Decoded Payload):
{
  "iss": "https://as.finance.com",
  "sub": "user-123",
  "aud": "https://api.finance.com",
  "exp": 1626003600,
  "iat": 1626000000,
  "scope": "transfer",
  "cnf": {
    "jkt": "N1hZ_-T4A48..._8I"
  }
}
  • SHA-256 Hash of this token (ath): fUHyO2r2Z3DZ53EsNrWBb0xWXoaNy59IiKCAqksmQEo

3. The DPoP Proof (Signed by the Browser) This is the JWT the browser will create for the POST /v1/transfer request.

  • DPoP Proof Header (includes the public key):
{
  "typ": "dpop+jwt",
  "alg": "ES256",
  "jwk": {
    "kty": "EC",
    "crv": "P-256",
    "x": "l8_224e-hNudx0j-gX-jTj_i-n2y-gX-jTj_i-n2y-gX",
    "y": "gX-jTj_i-n2y-gX-jTj_i-n2y-gX-jTj_i-n2y-gX-jT"
  }
}
  • DPoP Proof Payload:
{
  "jti": "unique-id-for-this-request-12345",
  "htm": "POST",
  "htu": "https://api.finance.com/v1/transfer",
  "iat": 1626003590,
  "ath": "fUHyO2r2Z3DZ53EsNrWBb0xWXoaNy59IiKCAqksmQEo"
}

Step A: Browser Creates the DPoP Proof

The browser cannot call the Resource Server directly and does not hold the access token. To authorize its request, it must first construct a valid DPoP proof by securely obtaining the access token hash (ath) from the BFF.

  1. Request the ath: Using the proactive "pull" model, the browser makes a request to the BFF's dedicated endpoint (e.g., GET /api/auth/ath), sending its HttpOnly session cookie.
  2. Receive the ath: The BFF validates the session cookie, retrieves the DPoP-bound access token from its secure store, computes its SHA-256 hash, and returns the hash (fUHyO2r2...) to the browser.
  3. Construct the Proof: The browser now builds the DPoP proof JWT using the payload defined above. It then signs this JWT with its private DPoP key.

Step B: Browser Calls the BFF’s API Endpoint

With the proof created, the browser sends its request to the BFF’s public-facing API endpoint, including the session cookie and the new proof.

Example HTTP Request to BFF:

POST /api/transfer HTTP/1.1
Host: bff.finance.com
Cookie: session=...
DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2Ii... (The new DPoP Proof)
Content-Type: application/json
{ "to": "account-B", "amount": 100.00 }

Step C: BFF Validates and Forwards to the Resource Server

The BFF acts as a secure intermediary, validating the browser’s request before forwarding it.

  1. Validate Session and Proof: The BFF validates the session cookie and retrieves the user’s public key associated with it. It then fully validates the signature and claims of the incoming DPoP proof from the browser.
  2. Construct the Final Request: The BFF now constructs the request to the actual Resource Server. It attaches the DPoP-bound access token it holds and forwards the browser’s original DPoP proof.

Example HTTP Request to Resource Server:

POST /v1/transfer HTTP/1.1
Host: api.finance.com
Authorization: DPoP eyJhbGciOiJSUzI1... (Access Token held by BFF)
DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2Ii... (Original DPoP Proof from browser)
Content-Type: application/json
{ "to": "account-B", "amount": 100.00 }

Step D: Resource Server Performs Final Validation

This is the final, rigorous check where the entire chain of possession is verified. The Resource Server now has the two critical artifacts defined at the start.

  1. Validate the DPoP Proof’s Signature: The RS uses the public key from the DPoP proof’s header to verify the proof’s signature.
  2. Validate the Proof’s Contextual Claims (Replay Prevention): The RS checks the proof’s payload to prevent replay attacks:
  • Is the iat timestamp recent?
  • Has the jti value been seen before?
  • Does the htm claim match the request method ("POST")?
  • Does the htu claim match the request URL ("https://api.finance.com/v1/transfer")?
  • Optionally, if the RS previously issued a nonce in a 401 response, it verifies that the proof contains a matching nonceclaim. This provides a strong guarantee against replay attacks, as only the browser possessing the private key could have solved the server's challenge to create the proof.

3. Validate the Access Token: The RS validates the access token’s signature, expiry (exp), and audience (aud).

4. Validate the Cryptographic Binding (The Core of DPoP):

  • Check the ath: The RS computes the SHA-256 hash of the received access token and confirms it exactly matches the ath claim (fUHyO2r2...) in the DPoP proof.
  • Check the jkt: The RS computes the JWK thumbprint of the public key from the DPoP proof's header and confirms it exactly matches the cnf.jkt claim (N1hZ_-T4...) in the access token.

Only when this entire chain of validation succeeds does the Resource Server trust the request and process the transaction.

Architectural Extension: End-to-End Payload Integrity with DPoP

1. Motivation and Threat Model

The core DPoP protocol (RFC 9449) provides robust sender-constraint for an access token, binding it to a specific key and request context (htm, htu). However, it does not inherently protect the integrity of the HTTP request body. This creates a significant vulnerability in a Backend-for-Frontend (BFF) architecture, particularly in a threat model that includes a compromised intermediary.

A malicious or compromised BFF, while unable to forge a DPoP proof without the browser’s key, could still intercept a legitimate request and tamper with its payload before forwarding it to the Resource Server (RS). For high-stakes operations — such as financial transfers, data modifications, or privacy settings changes — this is an unacceptable risk.

To close this gap, this architecture introduces an optional but critical extension: a custom **payload_hash* claim within the DPoP proof. This elevates the security guarantee from sender-constrained access to sender-guaranteed transaction integrity*, ensuring:

  • Non-repudiation: The Resource Server has cryptographic proof of the exact payload the user intended to send.
  • Tamper-Evidence: Any modification of the request body by an intermediary will cause the validation to fail.
  • End-to-End Integrity: The security chain extends from the browser’s user interface to the Resource Server’s business logic.

2. The payload_hash Claim Definition

The payload_hash is a custom claim added to the DPoP proof JWT payload.

  • Claim Name: payload_hash
  • Value: The Base64Url-encoded SHA-256 hash of the raw HTTP request body.

Example DPoP Proof Payload with payload_hash:

{
  "jti": "unique-id-for-this-request-12345",
  "htm": "POST",
  "htu": "<https://api.finance.com/v1/transfer>",
  "iat": 1626003590,
  "ath": "fUHyO2r2Z3DZ53EsNrWBb0xWXoaNy59IiKCAqksmQEo",
  "payload_hash": "Z_QhY1EzqHks3ReWBcW8WgNlMUG_9LdCT8dPILV6z8k"
}

Note on Standardization: As payload_hash is not a standard claim in RFC 9449, implementations should consider using a private claim namespace (e.g., https://your-rp.com/claims/payload_hash) or a prefix (x_payload_hash) to avoid collisions and ensure compatibility with strict JWT parsers.

3. Updated Architectural Flow and Responsibilities

The integration of this claim refines the responsibilities of each actor in the end-to-end flow.

a) The Browser: Origin of Integrity When constructing the DPoP proof for a critical request, the browser’s responsibilities are extended:

  1. Obtain ath: The browser securely fetches the access token hash from the BFF as usual.
  2. Generate Payload: It serializes the request body (e.g., JSON.stringify(payload)).
  3. Compute Hash: It computes the SHA-256 hash of the serialized payload.
  4. Construct and Sign Proof: It adds the resulting payload_hash to the DPoP proof's claims alongside the other required claims (ath, htu, etc.) and signs the complete JWT with its private key.

b) The BFF: A Transparent Conduit The BFF’s role in this flow is to be a transparent and secure proxy. It must notattempt to validate the payload_hash. Its sole responsibility is to forward the browser's DPoP proof and the request body to the Resource Server unmodified. This ensures that the BFF cannot tamper with the data without being detected.

c) The Resource Server: The Ultimate Verifier The Resource Server is the ultimate arbiter of the request’s integrity. It adds a final, crucial step to its DPoP validation sequence:

  1. It performs all standard DPoP validations (jkt, ath, signature, etc.).
  2. If a payload_hash claim is present in the proof, it computes the SHA-256 hash of the received HTTP request body.
  3. It compares its computed hash to the payload_hash value from the DPoP proof.
  4. If the values do not exactly match, the request must be rejected with a 401 Unauthorized error, as its integrity has been compromised.

4. Security Analysis

The inclusion of the payload_hash claim directly mitigates several critical attack vectors.

Attack VectorMitigation Provided by payload_hashData Tampering by Compromised BFFFully Mitigated. The RS will detect the mismatch between the proof's hash and the altered payload, rejecting the request.Replay Attack with Modified PayloadFully Mitigated. An attacker cannot reuse a captured proof with a different payload, as the hash would no longer match.Client-Side Logic ErrorsPartially Mitigated. Provides a server-side check that the data received is exactly what the client's hashing logic intended to send, helping to detect certain types of client-side bugs.

This extension completes the end-to-end assurance, binding the browser’s key not just to the token and the action, but also to the exact data payload of the transaction.

Conclusion

This paper has specified a comprehensive framework for creating high-assurance, end-to-end sender-constrained transactions in a browser-based environment. By orchestrating, rather than modifying, existing standards like OIDC, DPoP, and JAR, this architecture solves the fundamental vulnerability of bearer-token session cookies in modern web applications.

The proposal’s core contribution is a mechanism to establish and enforce a cryptographic binding between a user’s session and their specific browser instance. This is achieved by leveraging the OIDC authentication flow as a trusted ceremony to vouch for a browser-generated public key. The Relying Party uses this trusted key to issue a DPoP-bound session cookie, transforming it from a simple bearer token into a verifiable, sender-constrained credential.

With this secure foundation, the architecture ensures that every subsequent action — from API calls to token renewals — requires active, cryptographic proof-of-possession from the browser. Because the browser is the sole holder of the private key, this model guarantees that critical operations in high-stakes applications can only be performed by the legitimate user. Furthermore, by extending the DPoP proof to include a hash of the request body, the architecture provides a powerful mechanism for end-to-end payload integrity, ensuring that sensitive data cannot be modified in transit, even by a compromised intermediary. This framework provides a complete, high-confidence solution for systems where user control and transaction integrity are paramount.


메타데이터
post_id
ff6adb453e6f
slug
sender-constrained-web-security-from-dpop-bound-sessions-to-end-to-end-authorization-ff6adb453e6f
url
https://medium.com/@yveskerbs89/sender-constrained-web-security-from-dpop-bound-sessions-to-end-to-end-authorization-ff6adb453e6f
canonical_url
https://medium.com/@yveskerbs89/sender-constrained-web-security-from-dpop-bound-sessions-to-end-to-end-authorization-ff6adb453e6f
author_url
https://medium.com/@yveskerbs89
status
ok
fetched_at
2026-06-25 07:00:49