This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
securitycritical PriorityID: oauth21-dpop-rfc9449-binding-replay-guard

Enforce RFC 9449 DPoP Sender-Constrained Token Binding & Replay Guards

Mandates strict RFC 9449 Demonstrating Proof-of-Possession (DPoP) verification across zero-trust ingress boundaries to prevent token exfiltration and replay attacks. Ingress services must cryptographically bind access tokens to client-held asymmetric keys via thumbprint verification (cnf.jkt), validate proof claims (htm, htu, ath, iat), and enforce atomic JTI deduplication using a centralized distributed replay cache.

Prohibited Anti-Pattern: Accepting Unbound Bearer Tokens or Incomplete DPoP Proofs Without Atomic Replay Cache and Access Token Hash Binding

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: Enforce RFC 9449 DPoP Sender-Constrained Token Binding & Replay Guards - Mandates strict RFC 9449 Demonstrating Proof-of-Possession (DPoP) verification across zero-trust ingress boundaries to prevent token exfiltration and replay attacks. Ingress services must cryptographically bind access tokens to client-held asymmetric keys via thumbprint verification (cnf.jkt), validate proof claims (htm, htu, ath, iat), and enforce atomic JTI deduplication using a centralized distributed replay cache.
globs: src/auth/**/*.{ts,js}, src/middleware/**/*.{ts,js}, src/gateway/**/*.{ts,js}, src/security/**/*.{ts,js}
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/oauth21-dpop-rfc9449-binding-replay-guard"
---

# Enforce RFC 9449 DPoP Sender-Constrained Token Binding & Replay Guards

> **Severity**: CRITICAL | **Category**: security
> **Anti-Pattern Warning**: Accepting Unbound Bearer Tokens or Incomplete DPoP Proofs Without Atomic Replay Cache and Access Token Hash Binding

## System Constraints
You are a Principal Security Architect and Cryptography Engineer enforcing OAuth 2.1 RFC 9449 Sender-Constrained Tokens across microservice gateways. Enforce strict DPoP validation invariants: proof header verification (typ: 'dpop+jwt'), embedded JWK validation (disallow symmetric keys, require explicit curves/algorithms), payload validation (exact HTTP method 'htm', normalized HTTP URI 'htu' without query params, access token hash 'ath' via SHA-256 base64url), bounded issued-at timestamp checking ('iat' within tight clock skew), and distributed atomic JTI deduplication. Reject standard unconstrained Bearer tokens in DPoP-mandated scopes.

## Strict Guidelines
- Services MUST reject requests missing the 'DPoP' proof header or presenting standard 'Bearer' authorization when DPoP is required; the Authorization header scheme MUST be 'DPoP <token>'.
- Services MUST verify that the DPoP proof JOSE header 'typ' is exactly 'dpop+jwt' and contains an asymmetric public key in the 'jwk' header parameter.
- Services MUST NOT allow symmetric key algorithms (HS256, HS384, HS512) or 'none' in the DPoP proof; only asymmetric algorithms (ES256, EdDSA, RS256, etc.) are permitted.
- Services MUST compute the SHA-256 thumbprint (RFC 7638) of the public key provided in the DPoP proof JWK header and verify it matches the 'cnf.jkt' claim embedded within the access token.
- Services MUST compute the SHA-256 base64url-encoded hash of the raw access token string and verify exact equality against the 'ath' claim in the DPoP proof payload.
- Services MUST strictly match the DPoP proof 'htm' claim against the incoming HTTP method and 'htu' claim against the target URI (excluding query parameters and fragments).
- Services MUST reject DPoP proofs whose 'iat' timestamp exceeds strict clock-skew bounds (maximum 60 seconds drift).
- Services MUST record and check the 'jti' (JWT ID) in a distributed, atomic data store (e.g., Redis SETNX with TTL) to guarantee one-time use and eliminate replay attacks.

## Bad vs Good Implementations
### RFC 9449 DPoP Ingress Guardrail Verification Middleware

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: The middleware accepts standard Bearer tokens without requiring DPoP authentication, parses the DPoP header without cryptographically verifying the signature against the embedded JWK, omits the mandatory 'ath' access token hash validation, ignores the 'cnf.jkt' thumbprint matching, and performs no atomic JTI replay cache check, leaving tokens vulnerable to replay and exfiltration.
```typescript
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';

export function dpopAuthMiddleware(req: Request, res: Response, next: NextFunction) {
  const authHeader = req.headers.authorization;
  const dpopHeader = req.headers['dpop'] as string | undefined;

  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'invalid_token' });
  }

  const token = authHeader.substring(7);
  // VULNERABILITY 1: Allows bearer token without enforcing DPoP scheme
  // VULNERABILITY 2: Decodes DPoP without signature verification or matching JWK
  if (dpopHeader) {
    const decodedDpop = jwt.decode(dpopHeader) as any;
    // VULNERABILITY 3: Does not verify 'ath' (Access Token Hash)
    // VULNERABILITY 4: Ignores replay attacks (no distributed JTI tracking)
    if (decodedDpop && decodedDpop.htm !== req.method) {
      return res.status(401).json({ error: 'invalid_dpop_proof' });
    }
  }

  const claims = jwt.verify(token, process.env.JWT_PUBLIC_KEY!);
  (req as any).user = claims;
  next();
}
```

#### Production Standard (Correct)
> **Standard**: This implementation satisfies RFC 9449 requirements: enforces 'DPoP ' scheme, verifies the 'dpop+jwt' header type and algorithm whitelist, cryptographically checks the proof against the embedded public key, verifies method 'htm' and URI 'htu' without query params, computes the SHA-256 base64url 'ath' hash, eliminates replays via atomic Redis NX set with strict TTL, and validates that the token's 'cnf.jkt' thumbprint matches the public key that signed the proof.
```typescript
import { Request, Response, NextFunction } from 'express';
import crypto from 'crypto';
import * as jose from 'jose';
import { Redis } from 'ioredis';

const redis = new Redis(process.env.REDIS_URL!);
const SKEW_TOLERANCE_SECONDS = 60;
const ALLOWED_ALGORITHMS = ['ES256', 'ES384', 'EdDSA', 'RS256', 'PS256'];

export async function dpopAuthMiddleware(req: Request, res: Response, next: NextFunction): Promise<void> {
  const authHeader = req.headers.authorization;
  const dpopProofHeader = req.headers['dpop'] as string | undefined;

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

  const rawAccessToken = authHeader.substring(5).trim();

  try {
    // 1. Unpack protected header to retrieve embedded JWK
    const header = jose.decodeProtectedHeader(dpopProofHeader);
    if (header.typ !== 'dpop+jwt' || !header.jwk || !header.alg || !ALLOWED_ALGORITHMS.includes(header.alg)) {
      res.status(401).json({ error: 'invalid_dpop_proof', message: 'Invalid proof header or algorithm' });
      return;
    }

    const clientPublicKey = await jose.importJWK(header.jwk, header.alg);

    // 2. Verify signature and bounded lifespan
    const { payload } = await jose.jwtVerify(dpopProofHeader, clientPublicKey, {
      algorithms: ALLOWED_ALGORITHMS,
      clockTolerance: SKEW_TOLERANCE_SECONDS,
      maxTokenAge: `${SKEW_TOLERANCE_SECONDS}s`,
    });

    // 3. Validate HTTP Method (htm) and HTTP URI (htu)
    const expectedHtm = req.method.toUpperCase();
    const expectedHtu = `${req.protocol}://${req.get('host')}${req.path}`;

    if (payload.htm !== expectedHtm || payload.htu !== expectedHtu) {
      res.status(401).json({ error: 'invalid_dpop_proof', message: 'HTTP method or URI mismatch' });
      return;
    }

    // 4. Validate Access Token Hash (ath)
    const expectedAth = crypto.createHash('sha256').update(rawAccessToken, 'ascii').digest('base64url');
    if (payload.ath !== expectedAth) {
      res.status(401).json({ error: 'invalid_dpop_proof', message: 'Token hash mismatch' });
      return;
    }

    // 5. Atomic JTI Replay Prevention via Distributed Cache
    const jti = payload.jti;
    if (!jti) {
      res.status(401).json({ error: 'invalid_dpop_proof', message: 'Missing jti claim' });
      return;
    }
    const acquired = await redis.set(`dpop:jti:${jti}`, '1', 'EX', SKEW_TOLERANCE_SECONDS * 2, 'NX');
    if (!acquired) {
      res.status(401).json({ error: 'use_dpop_nonce', message: 'DPoP proof token replayed' });
      return;
    }

    // 6. Verify Access Token and bind cnf.jkt to DPoP JWK thumbprint
    const clientJwkThumbprint = await jose.calculateJwkThumbprint(header.jwk, 'sha256');
    const { payload: tokenClaims } = await jose.jwtVerify(rawAccessToken, jose.createRemoteJWKSet(new URL(process.env.JWKS_URI!)));

    const cnf = tokenClaims.cnf as { jkt?: string } | undefined;
    if (!cnf || cnf.jkt !== clientJwkThumbprint) {
      res.status(401).json({ error: 'invalid_token', message: 'Sender-constrained thumbprint mismatch' });
      return;
    }

    (req as any).user = tokenClaims;
    next();
  } catch (err) {
    res.status(401).json({ error: 'invalid_token', details: (err as Error).message });
  }
}
```

Recommended MCP Servers (mcp-serverlist.com)

Equip agent with system tools to verify code for this stack.

Recommended Agent Skills (askills.dev)

Ready-to-use executable skill functions tailored to this stack.

Architectural Specification & Invariant Rationale

Architectural Invariant Design: Enforce RFC 9449 DPoP Sender-Constrained Token Binding & Replay Guards

Enforce RFC 9449 DPoP Sender-Constrained Token Binding & Replay Guards defines an industrial-grade behavioral standard for security engineering. Mandates strict RFC 9449 Demonstrating Proof-of-Possession (DPoP) verification across zero-trust ingress boundaries to prevent token exfiltration and replay attacks. Ingress services must cryptographically bind access tokens to client-held asymmetric keys via thumbprint verification (cnf.jkt), validate proof claims (htm, htu, ath, iat), and enforce atomic JTI deduplication using a centralized distributed replay cache. 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 Unbound Bearer Tokens or Incomplete DPoP Proofs Without Atomic Replay Cache and Access Token Hash Binding 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 Unbound Bearer Tokens or Incomplete DPoP Proofs Without Atomic Replay Cache and Access Token Hash BindingCritical
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

Services MUST reject requests missing the 'DPoP' proof header or presenting standard 'Bearer' authorization when DPoP is required

Services MUST reject requests missing the 'DPoP' proof header or presenting standard 'Bearer' authorization when DPoP is required; the Authorization header scheme MUST be 'DPoP <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

Services MUST verify that the DPoP proof JOSE header 'typ' is exactly 'dpop+jwt' and contains an asymmetric public key in the 'jwk' header parameter.

Services MUST verify that the DPoP proof JOSE header 'typ' is exactly 'dpop+jwt' and contains an asymmetric public key in the 'jwk' header parameter.

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

Services MUST NOT allow symmetric key algorithms (HS256, HS384, HS512) or 'none' in the DPoP proof

Services MUST NOT allow symmetric key algorithms (HS256, HS384, HS512) or 'none' in the DPoP proof; only asymmetric algorithms (ES256, EdDSA, RS256, etc.) are permitted.

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

Services MUST compute the SHA-256 thumbprint (RFC 7638) of the public key provided in the DPoP proof JWK header and verify it matches the 'cnf.jkt' claim embedded within the access token.

Services MUST compute the SHA-256 thumbprint (RFC 7638) of the public key provided in the DPoP proof JWK header and verify it matches the 'cnf.jkt' claim embedded within the 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.
5

Services MUST compute the SHA-256 base64url-encoded hash of the raw access token string and verify exact equality against the 'ath' claim in the DPoP proof payload.

Services MUST compute the SHA-256 base64url-encoded hash of the raw access token string and verify exact equality against 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.
6

Services MUST strictly match the DPoP proof 'htm' claim against the incoming HTTP method and 'htu' claim against the target URI (excluding query parameters and fragments).

Services MUST strictly match the DPoP proof 'htm' claim against the incoming HTTP method and 'htu' claim against the target URI (excluding query parameters and fragments).

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

Services MUST reject DPoP proofs whose 'iat' timestamp exceeds strict clock-skew bounds (maximum 60 seconds drift).

Services MUST reject DPoP proofs whose 'iat' timestamp exceeds strict clock-skew bounds (maximum 60 seconds drift).

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.
8

Services MUST record and check the 'jti' (JWT ID) in a distributed, atomic data store (e.g., Redis SETNX with TTL) to guarantee one-time use and eliminate replay attacks.

Services MUST record and check the 'jti' (JWT ID) in a distributed, atomic data store (e.g., Redis SETNX with TTL) to guarantee one-time use and eliminate replay attacks.

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 Enforce RFC 9449 DPoP Sender-Constrained Token Binding & Replay Guards critical for AI coding assistants?

Mandates strict RFC 9449 Demonstrating Proof-of-Possession (DPoP) verification across zero-trust ingress boundaries to prevent token exfiltration and replay attacks. Ingress services must cryptographically bind access tokens to client-held asymmetric keys via thumbprint verification (cnf.jkt), validate proof claims (htm, htu, ath, iat), and enforce atomic JTI deduplication using a centralized distributed replay cache. 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 Unbound Bearer Tokens or Incomplete DPoP Proofs Without Atomic Replay Cache and Access Token Hash Binding) 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

Enforce