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

OAuth 2.0 DPoP (RFC 9449) Proof Token Replay & Clock-Skew Isolation Guardrail

Mandates end-to-end cryptographic verification of RFC 9449 Demonstrating Proof-of-Possession (DPoP) proof tokens across all edge and resource-tier API endpoints. Renders exfiltrated bearer tokens inert by strictly enforcing asymmetric key thumbprint ('cnf.jkt') binding, atomic single-use 'jti' replay tracking, strict clock-skew isolation windows, and canonical 'htm'/'htu' matching.

Prohibited Anti-Pattern: Accepting DPoP proofs without atomic jti replay checks, omitting cnf.jkt token binding, or permitting uncanonicalized URI validation.

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: OAuth 2.0 DPoP (RFC 9449) Proof Token Replay & Clock-Skew Isolation Guardrail - Mandates end-to-end cryptographic verification of RFC 9449 Demonstrating Proof-of-Possession (DPoP) proof tokens across all edge and resource-tier API endpoints. Renders exfiltrated bearer tokens inert by strictly enforcing asymmetric key thumbprint ('cnf.jkt') binding, atomic single-use 'jti' replay tracking, strict clock-skew isolation windows, and canonical 'htm'/'htu' matching.
globs: src/middleware/**/*.{ts,tsx}, src/auth/**/*.{ts,tsx}, src/security/**/*.{ts,tsx}, src/gateway/**/*.{ts,tsx}
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/oauth2-dpop-rfc9449-replay-isolation-standard"
---

# OAuth 2.0 DPoP (RFC 9449) Proof Token Replay & Clock-Skew Isolation Guardrail

> **Severity**: CRITICAL | **Category**: security
> **Anti-Pattern Warning**: Accepting DPoP proofs without atomic jti replay checks, omitting cnf.jkt token binding, or permitting uncanonicalized URI validation.

## System Constraints
You are a Principal Security Architect enforcing OAuth 2.0 DPoP (RFC 9449) Proof Token Replay & Clock-Skew Isolation Standards. Reject any pattern where bearer access tokens are accepted without cryptographic sender-constraining, where DPoP proof replay is not atomically mitigated, or where clock skew exceeds strict zero-trust thresholds.

## Strict Guidelines
- Engineers MUST ALWAYS verify that the DPoP proof JWT header contains 'typ': 'dpop+jwt' and an allowed asymmetric signature algorithm ('alg': 'ES256', 'ES384', 'ES512', 'PS256', or 'EdDSA'); symmetric algorithms ('HS*') and 'none' MUST be unconditionally rejected.
- Engineers MUST extract the public key embedded in the 'jwk' header of the DPoP proof, compute its SHA-256 JWK thumbprint ('jkt'), and verify it matches the 'cnf.jkt' claim present in the associated Access Token.
- Engineers MUST compute and verify the 'ath' (Access Token Hash) claim within the DPoP proof by comparing it to the base64url-encoded SHA-256 digest of the incoming access token.
- Engineers MUST enforce strict clock-skew bounds on the DPoP proof's 'iat' (Issued At) claim, rejecting tokens where '|now - iat| > 60 seconds'.
- Services MUST atomically enforce single-use replay protection by recording the proof's 'jti' (JWT ID) in a distributed cache (e.g., Redis via SET NX) with a TTL strictly matching the maximum clock-skew window.
- Engineers MUST validate that the 'htm' (HTTP Method) and 'htu' (HTTP URI) claims in the DPoP payload match the normalized incoming request method and canonical URI (protocol, host, port, path, excluding query and fragment).
- Components NEVER fall back to classic bearer token validation when a protected resource is configured for DPoP sender-constraining.

## Bad vs Good Implementations
### RFC 9449 DPoP Proof Token & Thumbprint Verification Middleware

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: The bad example decodes the DPoP proof without verifying its cryptographic signature against the embedded JWK, permits arbitrary algorithms, ignores the 'ath' (Access Token Hash) validation, skips 'jti' distributed replay caching, permits excessive clock skew, and fails to mathematically bind the proof to the access token's 'cnf.jkt' claim.
```typescript
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';

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

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

  // VULNERABILITY: Blindly decoding DPoP without validating header typ or verifying asymmetric key signature
  if (!dpopHeader) {
    return res.status(400).json({ error: 'missing_dpop_proof' });
  }
  
  const decodedDPoP = jwt.decode(dpopHeader) as any;
  const rawToken = authHeader.substring(5);
  const decodedToken = jwt.decode(rawToken) as any;

  // VULNERABILITY: No clock-skew enforcement (allowing replay years later)
  // VULNERABILITY: No distributed jti replay cache (attacker can replay DPoP token indefinitely)
  // VULNERABILITY: No ath (access token hash) comparison
  // VULNERABILITY: No cnf.jkt thumbprint verification between proof and access token
  if (decodedDPoP.htm !== req.method) {
    return res.status(400).json({ error: 'invalid_dpop_proof' });
  }

  next();
}
```

#### Production Standard (Correct)
> **Standard**: This production implementation complies with RFC 9449: it enforces 'typ: dpop+jwt', verifies the proof's asymmetric signature using the embedded JWK, validates absolute clock-skew limits (60s), ensures single-use replay protection via Redis SET NX, asserts HTTP method ('htm') and canonical URL ('htu'), binds the access token via SHA-256 'ath', and cryptographically matches the JWK thumbprint against the token's 'cnf.jkt'.
```typescript
import { Request, Response, NextFunction } from 'express';
import { createHash } from 'node: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_ALGS = ['ES256', 'ES384', 'ES512', 'PS256', 'EdDSA'];

function computeSha256Base64Url(input: string): string {
  return createHash('sha256').update(input, 'ascii').digest('base64url');
}

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

  if (typeof authHeader !== 'string' || !authHeader.startsWith('DPoP ')) {
    return res.status(401).setHeader('WWW-Authenticate', 'DPoP error="invalid_token"').send();
  }
  if (typeof dpopHeader !== 'string' || !dpopHeader) {
    return res.status(401).setHeader('WWW-Authenticate', 'DPoP error="invalid_dpop_proof"').send();
  }

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

  try {
    // 1. Inspect and validate header constraints
    const protectedHeader = jose.decodeProtectedHeader(dpopHeader);
    if (protectedHeader.typ !== 'dpop+jwt') {
      return res.status(400).json({ error: 'invalid_dpop_proof', error_description: 'typ must be dpop+jwt' });
    }
    if (!protectedHeader.alg || !ALLOWED_ALGS.includes(protectedHeader.alg)) {
      return res.status(400).json({ error: 'invalid_dpop_proof', error_description: 'Unsupported asymmetric algorithm' });
    }
    if (!protectedHeader.jwk || protectedHeader.jwk.kty === 'oct') {
      return res.status(400).json({ error: 'invalid_dpop_proof', error_description: 'Valid public JWK required' });
    }

    // 2. Import public key and cryptographically verify signature
    const publicKey = await jose.importJWK(protectedHeader.jwk, protectedHeader.alg);
    const { payload } = await jose.jwtVerify(dpopHeader, publicKey, {
      algorithms: ALLOWED_ALGS,
      clockTolerance: SKEW_TOLERANCE_SECONDS,
    });

    // 3. Strict Clock-Skew Boundaries
    const now = Math.floor(Date.now() / 1000);
    if (!payload.iat || Math.abs(now - payload.iat) > SKEW_TOLERANCE_SECONDS) {
      return res.status(400).json({ error: 'invalid_dpop_proof', error_description: 'Clock skew exceeds 60s window' });
    }

    // 4. Atomic JTI Replay Prevention (SET NX with TTL equal to skew window)
    if (!payload.jti) {
      return res.status(400).json({ error: 'invalid_dpop_proof', error_description: 'Missing jti claim' });
    }
    const replayKey = `dpop:jti:${payload.jti}`;
    const acquired = await redis.set(replayKey, '1', 'EX', SKEW_TOLERANCE_SECONDS * 2, 'NX');
    if (!acquired) {
      return res.status(401).json({ error: 'invalid_dpop_proof', error_description: 'DPoP proof token replay detected' });
    }

    // 5. HTM & Canonical HTU Validation
    if (payload.htm !== req.method.toUpperCase()) {
      return res.status(400).json({ error: 'invalid_dpop_proof', error_description: 'htm mismatch' });
    }
    const protocol = req.headers['x-forwarded-proto'] || req.protocol;
    const host = req.headers['x-forwarded-host'] || req.get('host');
    const canonicalUrl = `${protocol}://${host}${req.baseUrl}${req.path}`;
    if (payload.htu !== canonicalUrl) {
      return res.status(400).json({ error: 'invalid_dpop_proof', error_description: 'htu mismatch' });
    }

    // 6. Access Token Hash (ath) Validation
    const expectedAth = computeSha256Base64Url(accessToken);
    if (payload.ath !== expectedAth) {
      return res.status(400).json({ error: 'invalid_dpop_proof', error_description: 'ath mismatch' });
    }

    // 7. Cryptographic Thumbprint Binding (cnf.jkt verification)
    const expectedJkt = await jose.calculateJwkThumbprint(protectedHeader.jwk, 'sha256');
    const decodedToken = jose.decodeJwt(accessToken) as { cnf?: { jkt?: string } };
    if (!decodedToken.cnf?.jkt || decodedToken.cnf.jkt !== expectedJkt) {
      return res.status(401).json({ error: 'invalid_token', error_description: 'Token jkt binding mismatch' });
    }

    next();
  } catch (err) {
    return res.status(401).json({ error: 'invalid_dpop_proof', error_description: 'Verification failed' });
  }
}
```

Recommended MCP Servers (mcp-serverlist.com)

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

Architectural Specification & Invariant Rationale

Architectural Invariant Design: OAuth 2.0 DPoP (RFC 9449) Proof Token Replay & Clock-Skew Isolation Guardrail

OAuth 2.0 DPoP (RFC 9449) Proof Token Replay & Clock-Skew Isolation Guardrail defines an industrial-grade behavioral standard for security engineering. Mandates end-to-end cryptographic verification of RFC 9449 Demonstrating Proof-of-Possession (DPoP) proof tokens across all edge and resource-tier API endpoints. Renders exfiltrated bearer tokens inert by strictly enforcing asymmetric key thumbprint ('cnf.jkt') binding, atomic single-use 'jti' replay tracking, strict clock-skew isolation windows, and canonical 'htm'/'htu' matching. 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 DPoP proofs without atomic jti replay checks, omitting cnf.jkt token binding, or permitting uncanonicalized URI validation. 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 DPoP proofs without atomic jti replay checks, omitting cnf.jkt token binding, or permitting uncanonicalized URI validation.Critical
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

Engineers MUST ALWAYS verify that the DPoP proof JWT header contains 'typ'

Engineers MUST ALWAYS verify that the DPoP proof JWT header contains 'typ': 'dpop+jwt' and an allowed asymmetric signature algorithm ('alg': 'ES256', 'ES384', 'ES512', 'PS256', or 'EdDSA'); symmetric algorithms ('HS*') and 'none' MUST be unconditionally rejected.

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

Engineers MUST extract the public key embedded in the 'jwk' header of the DPoP proof, compute its SHA-256 JWK thumbprint ('jkt'), and verify it matches the 'cnf.jkt' claim present in the associated Access Token.

Engineers MUST extract the public key embedded in the 'jwk' header of the DPoP proof, compute its SHA-256 JWK thumbprint ('jkt'), and verify it matches the 'cnf.jkt' claim present in the associated 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.
3

Engineers MUST compute and verify the 'ath' (Access Token Hash) claim within the DPoP proof by comparing it to the base64url-encoded SHA-256 digest of the incoming access token.

Engineers MUST compute and verify the 'ath' (Access Token Hash) claim within the DPoP proof by comparing it to the base64url-encoded SHA-256 digest of the incoming 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

Engineers MUST enforce strict clock-skew bounds on the DPoP proof's 'iat' (Issued At) claim, rejecting tokens where '|now - iat| > 60 seconds'.

Engineers MUST enforce strict clock-skew bounds on the DPoP proof's 'iat' (Issued At) claim, rejecting tokens where '|now - iat| > 60 seconds'.

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 atomically enforce single-use replay protection by recording the proof's 'jti' (JWT ID) in a distributed cache (e.g., Redis via SET NX) with a TTL strictly matching the maximum clock-skew window.

Services MUST atomically enforce single-use replay protection by recording the proof's 'jti' (JWT ID) in a distributed cache (e.g., Redis via SET NX) with a TTL strictly matching the maximum clock-skew window.

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

Engineers MUST validate that the 'htm' (HTTP Method) and 'htu' (HTTP URI) claims in the DPoP payload match the normalized incoming request method and canonical URI (protocol, host, port, path, excluding query and fragment).

Engineers MUST validate that the 'htm' (HTTP Method) and 'htu' (HTTP URI) claims in the DPoP payload match the normalized incoming request method and canonical URI (protocol, host, port, path, excluding query and fragment).

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 fall back to classic bearer token validation when a protected resource is configured for DPoP sender-constraining.

Components NEVER fall back to classic bearer token validation when a protected resource is configured for DPoP sender-constraining.

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 OAuth 2.0 DPoP (RFC 9449) Proof Token Replay & Clock-Skew Isolation Guardrail critical for AI coding assistants?

Mandates end-to-end cryptographic verification of RFC 9449 Demonstrating Proof-of-Possession (DPoP) proof tokens across all edge and resource-tier API endpoints. Renders exfiltrated bearer tokens inert by strictly enforcing asymmetric key thumbprint ('cnf.jkt') binding, atomic single-use 'jti' replay tracking, strict clock-skew isolation windows, and canonical 'htm'/'htu' matching. 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 DPoP proofs without atomic jti replay checks, omitting cnf.jkt token binding, or permitting uncanonicalized URI validation.) 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

OAuth