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

OAuth 2.1 Asymmetric DPoP Proof Validation and Distributed Nonce Synchronization

Mandates strict Demonstrating Proof-of-Possession (DPoP, RFC 9449) validation across distributed API gateways, binding access tokens to client asymmetric keys via SHA-256 hashes (`ath`). Enforces cluster-wide synchronized server-issued nonces and atomic single-use JTI replay caches to neutralize cross-gateway token replay attacks upon token exfiltration.

Prohibited Anti-Pattern: Stateless or Localized DPoP Verification Allowing Cross-Gateway Token Replays and Unbound Bearer Token Substitution

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: OAuth 2.1 Asymmetric DPoP Proof Validation and Distributed Nonce Synchronization - Mandates strict Demonstrating Proof-of-Possession (DPoP, RFC 9449) validation across distributed API gateways, binding access tokens to client asymmetric keys via SHA-256 hashes (`ath`). Enforces cluster-wide synchronized server-issued nonces and atomic single-use JTI replay caches to neutralize cross-gateway token replay attacks upon token exfiltration.
globs: src/auth/**/*.{ts,tsx}, src/gateway/**/*.{ts,tsx}, src/middleware/**/*.{ts,tsx}, src/security/**/*.{ts,tsx}
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/oauth21-dpop-nonce-sync-replay-boundary-guardrail"
---

# OAuth 2.1 Asymmetric DPoP Proof Validation and Distributed Nonce Synchronization

> **Severity**: CRITICAL | **Category**: security
> **Anti-Pattern Warning**: Stateless or Localized DPoP Verification Allowing Cross-Gateway Token Replays and Unbound Bearer Token Substitution

## System Constraints
You are a Principal Security Architect enforcing OAuth 2.1 and RFC 9449 (DPoP) invariants across distributed API gateway topologies. You must prohibit conventional bearer token processing on protected endpoints requiring proof-of-possession. You must mandate distributed synchronization of DPoP nonces and atomic distributed JTI deduplication across all gateway nodes to prevent cross-node replay attacks.

## Strict Guidelines
- Engineers MUST ALWAYS enforce asymmetric DPoP token binding by validating that the `ath` (access token hash) claim matches the base64url-encoded SHA-256 digest of the incoming access token.
- Engineers MUST ALWAYS validate DPoP proofs against a cluster-synchronized distributed cache (e.g., Redis cluster) using atomic operations to prevent concurrent replay attacks across multiple gateway instances.
- Engineers MUST ALWAYS verify that the DPoP proof header contains a public JWK (`jwk`), verify that the key does NOT contain private key components (`d`, `p`, `q`), and reject symmetric algorithms (e.g., HS256) or weak asymmetric algorithms (e.g., none, RS1).
- Services MUST issue a cryptographically random, signed or cluster-verifiable `DPoP-Nonce` on missing or expired nonces, responding with HTTP 401 Unauthorized and standard error code `use_dpop_nonce`.
- Gateways MUST strictly compare the `htm` (HTTP method) and canonical `htu` (HTTP URI without query or fragment) claims against the received request context.
- Engineers MUST NOT maintain local, in-memory caches for DPoP JTI tracking or nonce generation in distributed multi-instance architectures.
- Components NEVER accept Bearer authorization schemes when DPoP is configured on the protected resource; requests MUST present the `DPoP <token>` authorization scheme.

## Bad vs Good Implementations
### Vulnerable: Stateless Local Memory DPoP Verification Missing Distributed Nonce Validation

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: The implementation relies on an in-memory `Set` for JTI tracking, allowing replay attacks across horizontally scaled gateway instances. It completely omits the mandatory `ath` (access token hash) validation (allowing an attacker to pair an arbitrary proof with any stolen token), skips server-side distributed nonce validation, and fails to canonicalize the `htu` URI.
```typescript
import { Request, Response, NextFunction } from 'express';
import * as jose from 'jose';

// VULNERABLE: In-memory replay cache fails across multiple gateway instances
const localJtiCache = new Set<string>();

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

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

  try {
    // VULNERABLE: Unprotected JWT header decoding, does not reject symmetric keys
    const unverifiedHeader = jose.decodeProtectedHeader(dpopProof);
    const publicKey = await jose.importJWK(unverifiedHeader.jwk as jose.JWK);
    const { payload } = await jose.jwtVerify(dpopProof, publicKey);

    // VULNERABLE: Local memory JTI check allows cross-gateway replay
    if (localJtiCache.has(payload.jti!)) {
      return res.status(401).json({ error: 'replay_detected' });
    }
    localJtiCache.add(payload.jti!);

    // VULNERABLE: Missing 'ath' check, missing distributed nonce check, missing URI normalization
    req.user = payload.sub;
    next();
  } catch (err) {
    return res.status(401).json({ error: 'invalid_dpop_proof' });
  }
}
```

#### Production Standard (Correct)
> **Standard**: This production-grade middleware enforces RFC 9449 compliance: it rejects symmetric keys and private key components, verifies the SHA-256 access token hash (`ath`), canonicalizes and checks `htm` and `htu`, uses atomic distributed Redis operations (`DEL` for single-use nonce consumption and `SET NX EX` for JTI replay defense), and issues new nonces via the `DPoP-Nonce` header.
```typescript
import { Request, Response, NextFunction } from 'express';
import * as crypto from 'crypto';
import * as jose from 'jose';
import type { Redis } from 'ioredis';

interface SecureDPoPOptions {
  redis: Redis;
  maxClockSkewSeconds?: number;
  nonceTtlSeconds?: number;
}

export function createSecureDPoPMiddleware({ redis, maxClockSkewSeconds = 60, nonceTtlSeconds = 300 }: SecureDPoPOptions) {
  return async function dpopMiddleware(req: Request, res: Response, next: NextFunction): Promise<void> {
    const dpopHeader = req.headers['dpop'] as string | undefined;
    const authHeader = req.headers['authorization'] as string | undefined;

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

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

    try {
      const header = jose.decodeProtectedHeader(dpopHeader);
      if (header.typ !== 'dpop+jwt' || !header.jwk || header.jwk.kty === 'oct') {
        res.status(400).json({ error: 'invalid_dpop_proof', error_description: 'Invalid header or symmetric key forbidden' });
        return;
      }

      // Enforce asymmetric key integrity without private components
      if ('d' in header.jwk || 'p' in header.jwk || 'q' in header.jwk) {
        res.status(400).json({ error: 'invalid_dpop_proof', error_description: 'Private key leakage in header' });
        return;
      }

      const publicKey = await jose.importJWK(header.jwk, header.alg);
      const { payload } = await jose.jwtVerify(dpopHeader, publicKey, {
        algorithms: ['ES256', 'ES384', 'ES512', 'PS256', 'PS384', 'PS512', 'EdDSA'],
        clockTolerance: maxClockSkewSeconds,
      });

      // 1. Mandatory HTTP Method & Canonical URI Binding
      const canonicalUrl = `${req.protocol}://${req.get('host')}${req.baseUrl}${req.path}`;
      if (payload.htm !== req.method.toUpperCase() || payload.htu !== canonicalUrl) {
        res.status(400).json({ error: 'invalid_dpop_proof', error_description: 'HTTP method or target URI mismatch' });
        return;
      }

      // 2. Mandatory Access Token Hash (ath) Validation
      const expectedAth = crypto.createHash('sha256').update(rawAccessToken).digest('base64url');
      if (payload.ath !== expectedAth) {
        res.status(401).json({ error: 'invalid_token', error_description: 'Access token hash (ath) mismatch' });
        return;
      }

      // 3. Distributed Nonce Validation via Redis
      const clientNonce = payload.nonce as string | undefined;
      if (!clientNonce) {
        const newNonce = crypto.randomBytes(32).toString('base64url');
        await redis.setex(`dpop:nonce:${newNonce}`, nonceTtlSeconds, '1');
        res.setHeader('DPoP-Nonce', newNonce);
        res.setHeader('WWW-Authenticate', 'DPoP error="use_dpop_nonce"');
        res.status(401).json({ error: 'use_dpop_nonce' });
        return;
      }

      const nonceKey = `dpop:nonce:${clientNonce}`;
      const consumed = await redis.del(nonceKey);
      if (consumed !== 1) {
        const refreshedNonce = crypto.randomBytes(32).toString('base64url');
        await redis.setex(`dpop:nonce:${refreshedNonce}`, nonceTtlSeconds, '1');
        res.setHeader('DPoP-Nonce', refreshedNonce);
        res.setHeader('WWW-Authenticate', 'DPoP error="use_dpop_nonce"');
        res.status(401).json({ error: 'use_dpop_nonce', error_description: 'Nonce invalid or expired' });
        return;
      }

      // 4. Distributed Atomic JTI Deduplication to enforce Replay Boundary
      const jtiKey = `dpop:jti:${payload.jti}`;
      const isNew = await redis.set(jtiKey, '1', 'EX', maxClockSkewSeconds * 2, 'NX');
      if (!isNew) {
        res.status(401).json({ error: 'invalid_dpop_proof', error_description: 'Proof replay detected' });
        return;
      }

      // Provide fresh DPoP-Nonce for subsequent requests
      const nextNonce = crypto.randomBytes(32).toString('base64url');
      await redis.setex(`dpop:nonce:${nextNonce}`, nonceTtlSeconds, '1');
      res.setHeader('DPoP-Nonce', nextNonce);

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

Architectural Specification & Invariant Rationale

Architectural Invariant Design: OAuth 2.1 Asymmetric DPoP Proof Validation and Distributed Nonce Synchronization

OAuth 2.1 Asymmetric DPoP Proof Validation and Distributed Nonce Synchronization defines an industrial-grade behavioral standard for security engineering. Mandates strict Demonstrating Proof-of-Possession (DPoP, RFC 9449) validation across distributed API gateways, binding access tokens to client asymmetric keys via SHA-256 hashes (`ath`). Enforces cluster-wide synchronized server-issued nonces and atomic single-use JTI replay caches to neutralize cross-gateway token replay attacks upon token exfiltration. 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: Stateless or Localized DPoP Verification Allowing Cross-Gateway Token Replays and Unbound Bearer Token Substitution 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 DriftStateless or Localized DPoP Verification Allowing Cross-Gateway Token Replays and Unbound Bearer Token SubstitutionCritical
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 asymmetric DPoP token binding by validating that the `ath` (access token hash) claim matches the base64url-encoded SHA-256 digest of the incoming access token.

Engineers MUST ALWAYS enforce asymmetric DPoP token binding by validating that the `ath` (access token hash) claim matches 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.
2

Engineers MUST ALWAYS validate DPoP proofs against a cluster-synchronized distributed cache (e.g., Redis cluster) using atomic operations to prevent concurrent replay attacks across multiple gateway instances.

Engineers MUST ALWAYS validate DPoP proofs against a cluster-synchronized distributed cache (e.g., Redis cluster) using atomic operations to prevent concurrent replay attacks across multiple gateway instances.

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 ALWAYS verify that the DPoP proof header contains a public JWK (`jwk`), verify that the key does NOT contain private key components (`d`, `p`, `q`), and reject symmetric algorithms (e.g., HS256) or weak asymmetric algorithms (e.g., none, RS1).

Engineers MUST ALWAYS verify that the DPoP proof header contains a public JWK (`jwk`), verify that the key does NOT contain private key components (`d`, `p`, `q`), and reject symmetric algorithms (e.g., HS256) or weak asymmetric algorithms (e.g., none, RS1).

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 issue a cryptographically random, signed or cluster-verifiable `DPoP-Nonce` on missing or expired nonces, responding with HTTP 401 Unauthorized and standard error code `use_dpop_nonce`.

Services MUST issue a cryptographically random, signed or cluster-verifiable `DPoP-Nonce` on missing or expired nonces, responding with HTTP 401 Unauthorized and standard error code `use_dpop_nonce`.

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

Gateways MUST strictly compare the `htm` (HTTP method) and canonical `htu` (HTTP URI without query or fragment) claims against the received request context.

Gateways MUST strictly compare the `htm` (HTTP method) and canonical `htu` (HTTP URI without query or fragment) claims against the received 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.
6

Engineers MUST NOT maintain local, in-memory caches for DPoP JTI tracking or nonce generation in distributed multi-instance architectures.

Engineers MUST NOT maintain local, in-memory caches for DPoP JTI tracking or nonce generation in distributed multi-instance architectures.

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 accept Bearer authorization schemes when DPoP is configured on the protected resource

Components NEVER accept Bearer authorization schemes when DPoP is configured on the protected resource; requests MUST present the `DPoP <token>` authorization scheme.

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.1 Asymmetric DPoP Proof Validation and Distributed Nonce Synchronization critical for AI coding assistants?

Mandates strict Demonstrating Proof-of-Possession (DPoP, RFC 9449) validation across distributed API gateways, binding access tokens to client asymmetric keys via SHA-256 hashes (`ath`). Enforces cluster-wide synchronized server-issued nonces and atomic single-use JTI replay caches to neutralize cross-gateway token replay attacks upon token exfiltration. 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 (Stateless or Localized DPoP Verification Allowing Cross-Gateway Token Replays and Unbound Bearer Token Substitution) 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