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

OAuth 2.0 DPoP Ephemeral Nonce-Binding and Replay Mitigation Standard

Enforces RFC 9449 Demonstrating Proof-of-Possession (DPoP) at resource and authorization servers to neutralize token theft and replay attacks. Requires asymmetric proof validation bound to client thumbprints, HTTP method and URI normalization, SHA-256 access token hashing, and strict ephemeral server-issued nonce rotation with single-use JTI cache enforcement.

Prohibited Anti-Pattern: Accepting Unbound Bearer Tokens or DPoP Proofs Missing Ephemeral Nonces and HTM/HTU Verifications

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: OAuth 2.0 DPoP Ephemeral Nonce-Binding and Replay Mitigation Standard - Enforces RFC 9449 Demonstrating Proof-of-Possession (DPoP) at resource and authorization servers to neutralize token theft and replay attacks. Requires asymmetric proof validation bound to client thumbprints, HTTP method and URI normalization, SHA-256 access token hashing, and strict ephemeral server-issued nonce rotation with single-use JTI cache enforcement.
globs: src/auth/**/*.ts, src/middleware/**/*.ts, src/security/**/*.ts, src/tokens/**/*.ts
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/oauth2-dpop-ephemeral-nonce-replay-mitigation"
---

# OAuth 2.0 DPoP Ephemeral Nonce-Binding and Replay Mitigation Standard

> **Severity**: CRITICAL | **Category**: security
> **Anti-Pattern Warning**: Accepting Unbound Bearer Tokens or DPoP Proofs Missing Ephemeral Nonces and HTM/HTU Verifications

## System Constraints
You are a Principal Security Architect enforcing RFC 9449 OAuth 2.0 Demonstrating Proof-of-Possession (DPoP). Every protected endpoint must reject traditional bearer authorization when DPoP is required. You must ensure incoming DPoP proofs are validated for header typing ('dpop+jwt'), asymmetric algorithm restrictions (rejecting 'none' and symmetric MACs), normalized 'htm' and 'htu' claims matching the execution context, computed 'ath' SHA-256 hashes against presented access tokens, non-replayed 'jti' identifiers, and cryptographically verified server-issued ephemeral nonces via 'DPoP-Nonce'. If a nonce is invalid or expired, reject immediately with HTTP 401 and issue a fresh 'DPoP-Nonce' header.

## Strict Guidelines
- Engineers MUST ALWAYS enforce the 'dpop+jwt' header type and restrict algorithms to asymmetric curves (e.g., ES256, EdDSA, PS256), explicitly rejecting symmetric or 'none' algorithms.
- Engineers MUST ALWAYS compute the SHA-256 hash of the presented access token ('ath' claim) and verify equality against the DPoP proof claim in constant time.
- Services MUST validate the 'htm' (HTTP method) and 'htu' (HTTP URI without query or fragment) claims against the current normalized request context.
- Services MUST verify the server-issued ephemeral 'nonce' within the proof and issue a new 'DPoP-Nonce' response header on initial connection or expiry.
- Services MUST track the proof's 'jti' in an ephemeral distributed cache (e.g., Redis with TTL matching max clock skew) to prevent in-flight replay attacks within the allowable time window.
- Components NEVER store private DPoP keys in persistent or insecure client storage; keys must reside in WebCrypto non-extractable storage, Hardware Security Modules (HSM), or secure enclaves.

## Bad vs Good Implementations
### DPoP Verification Middleware with Nonce & Replay Enforcement

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Accepts DPoP proofs without verifying the signature against the embedded ephemeral JWK public key, ignores the 'ath' access token hash binding, allows replay attacks by skipping 'jti' state tracking, and does not validate dynamic server-issued nonces.
```typescript
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';

// ANTI-PATTERN: Blindly trusting Bearer tokens or incomplete DPoP validation
export async function verifyAuth(req: Request, res: Response, next: NextFunction) {
  const authHeader = req.headers.authorization;
  if (!authHeader?.startsWith('DPoP ')) {
    return res.status(401).json({ error: 'invalid_token' });
  }
  
  const rawProof = req.headers['dpop'] as string;
  if (!rawProof) {
    return res.status(401).json({ error: 'missing_dpop_proof' });
  }

  // VULNERABILITY: Decoding without verifying signature against JWK,
  // ignoring htm/htu binding, skipping 'ath' verification, and accepting static proofs without nonce check
  const decodedProof = jwt.decode(rawProof) as { jti: string; htm: string; htu: string };
  if (decodedProof.htm !== req.method) {
    return res.status(400).json({ error: 'invalid_htm' });
  }

  return next();
}
```

#### Production Standard (Correct)
> **Standard**: Implements full RFC 9449 compliance: validates 'dpop+jwt' typing and asymmetric algorithms, checks normalized 'htm' and 'htu', performs timing-safe verification of the 'ath' token hash, enforces single-use ephemeral server nonces via the 'DPoP-Nonce' header, records 'jti' identifiers to prevent in-flight replays, and computes JWK thumbprints for downstream client binding.
```typescript
import { Request, Response, NextFunction } from 'express';
import crypto from 'node:crypto';
import * as jose from 'jose';

interface NonceStore {
  consumeNonce(nonce: string): Promise<boolean>;
  createNonce(): Promise<string>;
  hasSeenJti(jti: string): Promise<boolean>;
  recordJti(jti: string, ttlSeconds: number): Promise<void>;
}

declare const nonceStore: NonceStore;
const ALLOWED_CLOCK_SKEW_SEC = 60;

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

  if (!authHeader?.startsWith('DPoP ') || typeof dpopHeader !== 'string') {
    const newNonce = await nonceStore.createNonce();
    res.setHeader('DPoP-Nonce', newNonce);
    res.status(401).json({ error: 'use_dpop_nonce', message: 'DPoP proof required' });
    return;
  }

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

  try {
    // 1. Parse header without validation to inspect JWK and algorithm
    const protectedHeader = jose.decodeProtectedHeader(dpopHeader);
    if (protectedHeader.typ !== 'dpop+jwt' || !protectedHeader.jwk) {
      res.status(400).json({ error: 'invalid_dpop_proof', message: 'Invalid typ or missing jwk' });
      return;
    }

    const jwk = protectedHeader.jwk;
    if (jwk.kty !== 'EC' && jwk.kty !== 'OKP' && jwk.kty !== 'RSA') {
      res.status(400).json({ error: 'invalid_dpop_proof', message: 'Unsupported key type' });
      return;
    }

    // 2. Import public key and verify signature
    const publicKey = await jose.importJWK(jwk, protectedHeader.alg);
    const { payload } = await jose.jwtVerify(dpopHeader, publicKey, {
      maxTokenAge: `${ALLOWED_CLOCK_SKEW_SEC}s`,
      algorithms: ['ES256', 'PS256', 'EdDSA']
    });

    // 3. Validate HTM and HTU
    const currentUri = `${req.protocol}://${req.get('host')}${req.baseUrl}${req.path}`;
    if (payload.htm !== req.method || payload.htu !== currentUri) {
      res.status(400).json({ error: 'invalid_dpop_proof', message: 'HTM or HTU mismatch' });
      return;
    }

    // 4. Validate Access Token Hash (ath)
    const expectedAth = crypto.createHash('sha256').update(accessToken).digest('base64url');
    const proofAth = typeof payload.ath === 'string' ? payload.ath : '';
    if (!crypto.timingSafeEqual(Buffer.from(proofAth), Buffer.from(expectedAth))) {
      res.status(400).json({ error: 'invalid_dpop_proof', message: 'ath mismatch' });
      return;
    }

    // 5. Verify and atomically consume server-issued Nonce
    const proofNonce = typeof payload.nonce === 'string' ? payload.nonce : '';
    const isValidNonce = await nonceStore.consumeNonce(proofNonce);
    if (!isValidNonce) {
      const freshNonce = await nonceStore.createNonce();
      res.setHeader('DPoP-Nonce', freshNonce);
      res.status(401).json({ error: 'use_dpop_nonce', message: 'Nonce expired or invalid' });
      return;
    }

    // 6. Enforce JTI replay prevention
    const jti = payload.jti;
    if (!jti || await nonceStore.hasSeenJti(jti)) {
      res.status(401).json({ error: 'invalid_dpop_proof', message: 'jti replay detected' });
      return;
    }
    await nonceStore.recordJti(jti, ALLOWED_CLOCK_SKEW_SEC * 2);

    // Attach validated client thumbprint for downstream identity binding
    const thumbprint = await jose.calculateJwkThumbprint(jwk);
    req.app.locals.dpopThumbprint = thumbprint;

    next();
  } catch (err) {
    const freshNonce = await nonceStore.createNonce();
    res.setHeader('DPoP-Nonce', freshNonce);
    res.status(401).json({ error: 'invalid_dpop_proof', message: 'Cryptographic proof failed' });
  }
}
```

Architectural Specification & Invariant Rationale

Architectural Invariant Design: OAuth 2.0 DPoP Ephemeral Nonce-Binding and Replay Mitigation Standard

OAuth 2.0 DPoP Ephemeral Nonce-Binding and Replay Mitigation Standard defines an industrial-grade behavioral standard for security engineering. Enforces RFC 9449 Demonstrating Proof-of-Possession (DPoP) at resource and authorization servers to neutralize token theft and replay attacks. Requires asymmetric proof validation bound to client thumbprints, HTTP method and URI normalization, SHA-256 access token hashing, and strict ephemeral server-issued nonce rotation with single-use JTI cache enforcement. 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 DPoP Proofs Missing Ephemeral Nonces and HTM/HTU Verifications 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 DPoP Proofs Missing Ephemeral Nonces and HTM/HTU VerificationsCritical
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 enforce the 'dpop+jwt' header type and restrict algorithms to asymmetric curves (e.g., ES256, EdDSA, PS256), explicitly rejecting symmetric or 'none' algorithms.

Engineers MUST ALWAYS enforce the 'dpop+jwt' header type and restrict algorithms to asymmetric curves (e.g., ES256, EdDSA, PS256), explicitly rejecting symmetric or 'none' algorithms.

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 ALWAYS compute the SHA-256 hash of the presented access token ('ath' claim) and verify equality against the DPoP proof claim in constant time.

Engineers MUST ALWAYS compute the SHA-256 hash of the presented access token ('ath' claim) and verify equality against the DPoP proof claim in constant time.

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 validate the 'htm' (HTTP method) and 'htu' (HTTP URI without query or fragment) claims against the current normalized request context.

Services MUST validate the 'htm' (HTTP method) and 'htu' (HTTP URI without query or fragment) claims against the current normalized request context.

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 verify the server-issued ephemeral 'nonce' within the proof and issue a new 'DPoP-Nonce' response header on initial connection or expiry.

Services MUST verify the server-issued ephemeral 'nonce' within the proof and issue a new 'DPoP-Nonce' response header on initial connection or expiry.

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 track the proof's 'jti' in an ephemeral distributed cache (e.g., Redis with TTL matching max clock skew) to prevent in-flight replay attacks within the allowable time window.

Services MUST track the proof's 'jti' in an ephemeral distributed cache (e.g., Redis with TTL matching max clock skew) to prevent in-flight replay attacks within the allowable time 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

Components NEVER store private DPoP keys in persistent or insecure client storage

Components NEVER store private DPoP keys in persistent or insecure client storage; keys must reside in WebCrypto non-extractable storage, Hardware Security Modules (HSM), or secure enclaves.

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 Ephemeral Nonce-Binding and Replay Mitigation Standard critical for AI coding assistants?

Enforces RFC 9449 Demonstrating Proof-of-Possession (DPoP) at resource and authorization servers to neutralize token theft and replay attacks. Requires asymmetric proof validation bound to client thumbprints, HTTP method and URI normalization, SHA-256 access token hashing, and strict ephemeral server-issued nonce rotation with single-use JTI cache enforcement. 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 DPoP Proofs Missing Ephemeral Nonces and HTM/HTU Verifications) 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