This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
securitycritical PriorityID: rfc-9449-dpop-sender-constrained-enforcement

RFC 9449 DPoP Sender-Constrained Token Enforcement Fence

Enforces cryptographic binding between OAuth 2.0 access tokens and client-generated asymmetric key pairs using RFC 9449 Demonstrating Proof-of-Possession (DPoP). Guarantees that token exfiltration across untrusted networks or compromised storage cannot lead to credential replay attacks against downstream resource servers. Requires strict verification of public key thumbprints, HTTP method/URI bindings, access token hashes, and distributed proof replay prevention.

Prohibited Anti-Pattern: Accepting Bearer Tokens Without Cryptographic Sender-Binding or Permitting Unverified DPoP Proofs

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: RFC 9449 DPoP Sender-Constrained Token Enforcement Fence - Enforces cryptographic binding between OAuth 2.0 access tokens and client-generated asymmetric key pairs using RFC 9449 Demonstrating Proof-of-Possession (DPoP). Guarantees that token exfiltration across untrusted networks or compromised storage cannot lead to credential replay attacks against downstream resource servers. Requires strict verification of public key thumbprints, HTTP method/URI bindings, access token hashes, and distributed proof replay prevention.
globs: src/middleware/**/*.{ts,tsx}, src/auth/**/*.{ts,tsx}, src/security/**/*.{ts,tsx}, src/controllers/**/*.{ts,tsx}, src/api/**/*.{ts,tsx}
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/rfc-9449-dpop-sender-constrained-enforcement"
---

# RFC 9449 DPoP Sender-Constrained Token Enforcement Fence

> **Severity**: CRITICAL | **Category**: security
> **Anti-Pattern Warning**: Accepting Bearer Tokens Without Cryptographic Sender-Binding or Permitting Unverified DPoP Proofs

## System Constraints
You are a Principal Security Architect enforcing RFC 9449 DPoP (Demonstrating Proof-of-Possession) sender-constrained token enforcement fences across all HTTP API layers. Replay attacks and bearer token exfiltration MUST be mathematically and cryptographically precluded. Enforce strict public key thumbprint matching (cnf.jkt), access token hashing (ath), HTTP request matching (htm/htu), and distributed replay mitigation.

## Strict Guidelines
- Resource servers MUST reject requests using standard 'Bearer' authorization schemes when DPoP is mandated; authorization header MUST adhere to 'DPoP <access_token>'.
- Resource servers MUST extract the 'DPoP' header and verify that the proof is a valid signed JWT containing a supported asymmetric 'jwk' header parameter without 'x5c' or private key material.
- The resource server MUST calculate the base64url-encoded SHA-256 thumbprint (RFC 7638) of the DPoP proof's public key and strictly assert equality against the 'cnf.jkt' claim embedded in the presented access token.
- The resource server MUST compute the base64url-encoded SHA-256 hash of the presented raw access token string and assert byte-for-byte equality with the 'ath' claim in the DPoP proof payload.
- The proof payload claims 'htm' (HTTP method) and 'htu' (HTTP URI) MUST match the incoming request method and canonical URI (protocol, host, and path; stripping query params and fragments) case-insensitively for the URI and case-sensitively for the method.
- Resource servers MUST enforce replay protection: evaluate 'iat' within a strict clock-skew tolerance (maximum 60 seconds) and check 'jti' against a distributed deduplication store with an explicit TTL.
- Components NEVER store private DPoP keys in persistent client-side storage accessible to unauthorized code or third-party scripts (e.g., raw localStorage); use Web Crypto API Non-Exportable CryptoKeys or hardware-backed stores.

## Bad vs Good Implementations
### Enforce RFC 9449 DPoP Proof and Token Binding Middleware

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Accepts unsecured Bearer tokens without DPoP sender-binding. Any attacker intercepting the token can replay it against the resource server without possessing the client's private key. The endpoint fails to validate 'ath', 'htm', 'htu', or the JWK thumbprint ('cnf.jkt').
```typescript
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';

// ANTI-PATTERN: Accepts standard bearer token without sender-constraining
// Vulnerable to token exfiltration, lack of proof-of-possession, and replay attacks
export async function authenticateResource(req: Request, res: Response, next: NextFunction) {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'invalid_token' });
  }

  const token = authHeader.substring(7);
  try {
    // Verifies token signature but does not bind request to caller's cryptographic key
    const claims = jwt.verify(token, process.env.JWT_SECRET as string);
    (req as any).user = claims;
    return next();
  } catch (err) {
    return res.status(401).json({ error: 'invalid_token' });
  }
}
```

#### Production Standard (Correct)
> **Standard**: Follows RFC 9449 requirements: mandates the 'DPoP' scheme, verifies the token signature against remote JWKS, calculates the RFC 7638 JWK thumbprint and verifies match with 'cnf.jkt', matches the SHA-256 'ath' hash, verifies canonical 'htm' and 'htu' values, and maintains a distributed anti-replay barrier on proof 'jti' within an acceptable time skew.
```typescript
import { Request, Response, NextFunction } from 'express';
import { createHash } from 'node:crypto';
import * as jose from 'jose';

interface TokenClaims extends jose.JWTPayload {
  cnf?: { jkt: string };
}

interface ReplayCache {
  has(jti: string): Promise<boolean>;
  set(jti: string, ttlSeconds: number): Promise<void>;
}

declare const replayCache: ReplayCache;
const MAX_CLOCK_SKEW_SECONDS = 60;

export function createDPoPVerifier(asymmetricJwksUri: string) {
  const JWKS = jose.createRemoteJWKSet(new URL(asymmetricJwksUri));

  return async function verifyDPoPRequest(req: Request, res: Response, next: NextFunction) {
    const authHeader = req.headers.authorization;
    const dpopHeader = req.headers['dpop'];

    if (!authHeader || !authHeader.startsWith('DPoP ')) {
      res.setHeader('WWW-Authenticate', 'DPoP error="invalid_token", error_description="DPoP scheme required"');
      return res.status(401).json({ error: 'invalid_token' });
    }

    if (typeof dpopHeader !== 'string') {
      res.setHeader('WWW-Authenticate', 'DPoP error="invalid_dpop_proof"');
      return res.status(401).json({ error: 'invalid_dpop_proof' });
    }

    const accessToken = authHeader.slice(5).trim();

    try {
      // 1. Verify Access Token authenticity
      const { payload: tokenClaims } = await jose.jwtVerify<TokenClaims>(accessToken, JWKS);
      const expectedJkt = tokenClaims.cnf?.jkt;
      if (!expectedJkt) {
        return res.status(401).json({ error: 'invalid_token', error_description: 'Token not DPoP bound (missing cnf.jkt)' });
      }

      // 2. Decode and verify the DPoP Proof JWT using its embedded JWK
      const { payload: dpopPayload, protectedHeader } = await jose.jwtVerify(dpopHeader, async (header) => {
        if (header.typ !== 'dpop+jwt') throw new Error('Invalid DPoP typ header');
        if (!header.jwk || typeof header.jwk !== 'object') throw new Error('Missing JWK in DPoP header');
        return jose.importJWK(header.jwk, header.alg);
      });

      // 3. Cryptographically bind public key thumbprint to token's cnf.jkt claim
      const calculatedJkt = await jose.calculateJwkThumbprint(protectedHeader.jwk as jose.JWK, 'sha256');
      if (calculatedJkt !== expectedJkt) {
        return res.status(401).json({ error: 'invalid_dpop_proof', error_description: 'Key thumbprint mismatch' });
      }

      // 4. Validate Access Token Hash ('ath')
      const computedAth = createHash('sha256').update(accessToken).digest('base64url');
      if (dpopPayload.ath !== computedAth) {
        return res.status(401).json({ error: 'invalid_dpop_proof', error_description: 'Access token hash (ath) mismatch' });
      }

      // 5. Assert HTTP Method ('htm') and HTTP URI ('htu') integrity
      const fullUri = `${req.protocol}://${req.get('host')}${req.baseUrl}${req.path}`;
      if (dpopPayload.htm !== req.method) {
        return res.status(401).json({ error: 'invalid_dpop_proof', error_description: 'HTTP method mismatch' });
      }
      if (typeof dpopPayload.htu !== 'string' || dpopPayload.htu.toLowerCase() !== fullUri.toLowerCase()) {
        return res.status(401).json({ error: 'invalid_dpop_proof', error_description: 'HTTP URI mismatch' });
      }

      // 6. Anti-replay validation on proof identifier (jti) and time-window (iat)
      const proofIat = dpopPayload.iat;
      const now = Math.floor(Date.now() / 1000);
      if (!proofIat || Math.abs(now - proofIat) > MAX_CLOCK_SKEW_SECONDS) {
        return res.status(401).json({ error: 'invalid_dpop_proof', error_description: 'Proof timestamp outside validity window' });
      }

      const jti = dpopPayload.jti;
      if (!jti || await replayCache.has(jti)) {
        return res.status(401).json({ error: 'invalid_dpop_proof', error_description: 'Proof replayed or missing jti' });
      }
      await replayCache.set(jti, MAX_CLOCK_SKEW_SECONDS * 2);

      (req as any).user = tokenClaims;
      return next();
    } catch (err: unknown) {
      return res.status(401).json({ error: 'invalid_dpop_proof' });
    }
  };
}
```

Architectural Specification & Invariant Rationale

Architectural Invariant Design: RFC 9449 DPoP Sender-Constrained Token Enforcement Fence

RFC 9449 DPoP Sender-Constrained Token Enforcement Fence defines an industrial-grade behavioral standard for security engineering. Enforces cryptographic binding between OAuth 2.0 access tokens and client-generated asymmetric key pairs using RFC 9449 Demonstrating Proof-of-Possession (DPoP). Guarantees that token exfiltration across untrusted networks or compromised storage cannot lead to credential replay attacks against downstream resource servers. Requires strict verification of public key thumbprints, HTTP method/URI bindings, access token hashes, and distributed proof replay prevention. Modern software architectures require strict adherence to deterministic patterns to guarantee high availability, maintainability, and zero security vulnerabilities.

Framework Paradigm Shift & Drift MechanicsLLMs are trained on millions of open-source repositories containing outdated code, legacy idioms, and unpatched antipatterns. When coding assistants generate implementations for this stack, they default to common statistical tokens: Accepting Bearer Tokens Without Cryptographic Sender-Binding or Permitting Unverified DPoP Proofs Without an explicit system instruction file, developers spend hours debugging silent failures.
Failure Modes & Production Blast Radius

Hazard Analysis: What Breaks Without This Rule

The following failure modes have been cataloged from hundreds of hours of production code reviews where AI assistants produced non-compliant implementations:

Failure ModeAI Symptom (The Anti-Pattern)SeverityProduction Impact & Remedy
AI Hallucination & Deprecation DriftAccepting Bearer Tokens Without Cryptographic Sender-Binding or Permitting Unverified DPoP ProofsCritical
Violating this industrial standard introduces regression bugs, security risks, and unexpected runtime failures in production.
Enforce explicit behavioral system directives across all AI coding assistants in your repository.
Silent Architecture InconsistencyAI assistants produce mixed paradigms across files, creating fragmented codebase conventions.Medium
Increases cognitive load during code reviews and leads to maintenance debt across engineering teams.
Pin project conventions in .cursor/rules, CLAUDE.md, and AGENTS.md so all team agents follow identical guardrails.
Architectural Directives in Detail

Step-by-Step Production Directives

Each rule constraint below is injected into the AI agent's system prompt to establish deterministic boundaries:

1

Resource servers MUST reject requests using standard 'Bearer' authorization schemes when DPoP is mandated

Resource servers MUST reject requests using standard 'Bearer' authorization schemes when DPoP is mandated; authorization header MUST adhere to 'DPoP <access_token>'.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
2

Resource servers MUST extract the 'DPoP' header and verify that the proof is a valid signed JWT containing a supported asymmetric 'jwk' header parameter without 'x5c' or private key material.

Resource servers MUST extract the 'DPoP' header and verify that the proof is a valid signed JWT containing a supported asymmetric 'jwk' header parameter without 'x5c' or private key material.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
3

The resource server MUST calculate the base64url-encoded SHA-256 thumbprint (RFC 7638) of the DPoP proof's public key and strictly assert equality against the 'cnf.jkt' claim embedded in the presented access token.

The resource server MUST calculate the base64url-encoded SHA-256 thumbprint (RFC 7638) of the DPoP proof's public key and strictly assert equality against the 'cnf.jkt' claim embedded in the presented access token.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
4

The resource server MUST compute the base64url-encoded SHA-256 hash of the presented raw access token string and assert byte-for-byte equality with the 'ath' claim in the DPoP proof payload.

The resource server MUST compute the base64url-encoded SHA-256 hash of the presented raw access token string and assert byte-for-byte equality with the 'ath' claim in the DPoP proof payload.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
5

The proof payload claims 'htm' (HTTP method) and 'htu' (HTTP URI) MUST match the incoming request method and canonical URI (protocol, host, and path

The proof payload claims 'htm' (HTTP method) and 'htu' (HTTP URI) MUST match the incoming request method and canonical URI (protocol, host, and path; stripping query params and fragments) case-insensitively for the URI and case-sensitively for the method.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
6

Resource servers MUST enforce replay protection

Resource servers MUST enforce replay protection: evaluate 'iat' within a strict clock-skew tolerance (maximum 60 seconds) and check 'jti' against a distributed deduplication store with an explicit TTL.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
7

Components NEVER store private DPoP keys in persistent client-side storage accessible to unauthorized code or third-party scripts (e.g., raw localStorage)

Components NEVER store private DPoP keys in persistent client-side storage accessible to unauthorized code or third-party scripts (e.g., raw localStorage); use Web Crypto API Non-Exportable CryptoKeys or hardware-backed stores.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
Technical Questions & Answers

Frequently Asked Technical Questions

Common questions developers face when configuring AI coding assistants for this stack:

Q:Why is RFC 9449 DPoP Sender-Constrained Token Enforcement Fence critical for AI coding assistants?

Enforces cryptographic binding between OAuth 2.0 access tokens and client-generated asymmetric key pairs using RFC 9449 Demonstrating Proof-of-Possession (DPoP). Guarantees that token exfiltration across untrusted networks or compromised storage cannot lead to credential replay attacks against downstream resource servers. Requires strict verification of public key thumbprints, HTTP method/URI bindings, access token hashes, and distributed proof replay prevention. Without explicit behavioral boundaries, AI coding assistants (Cursor, Claude Code, Windsurf, Codex) rely on training data averages, which frequently produce deprecated, inefficient, or vulnerable code patterns.

Q:How does adopting this rule prevent AI hallucinations?

This rule explicitly declares what the AI must avoid (Accepting Bearer Tokens Without Cryptographic Sender-Binding or Permitting Unverified DPoP Proofs) and prescribes verified positive examples. Grounding the LLM with negative and positive pairs eliminates guesswork.

Q:Which AI coding environments are supported?

This specification is multi-platform compatible: Cursor (.cursor/rules/*.mdc & .cursorrules), Anthropic Claude Code (CLAUDE.md), Windsurf (.windsurfrules), Codex / Cline (AGENTS.md), and GitHub Copilot (.github/copilot-instructions.md).

Q:Can I combine this rule with other technology stacks?

Yes. Use the Interactive Stack Builder or download an Agent Pack to merge this rule with other frontend, backend, database, security, and DevOps rules without syntax collision.

Open Specification · CC BY 4.0·Free for Personal, Team & Commercial In-Repo Use

Citation & Open Source Attribution Guidelines

This specification is licensed under Creative Commons Attribution 4.0 International (CC BY 4.0). You are completely free to adapt and share this rule in your workspace. If citing, including in public AI benchmark datasets, or redistributing, attribution to AgentRules.org is required.

1 Rule in Stack

RFC