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

RFC 9449 DPoP Cryptographic Proof and Nonce Binding Standard

Enforces application-layer proof-of-possession for OAuth 2.0 access tokens and mutation endpoints via RFC 9449 DPoP. Requires strict asymmetric key binding using non-extractable client keys, verification of HTTP method and URI claims, hash-bound access tokens ('ath'), and synchronized ephemeral server-issued nonces to eliminate exfiltration and replay attacks.

Prohibited Anti-Pattern: Unbound Bearer Token Forwarding and Missing Ephemeral DPoP Nonce Validation

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: RFC 9449 DPoP Cryptographic Proof and Nonce Binding Standard - Enforces application-layer proof-of-possession for OAuth 2.0 access tokens and mutation endpoints via RFC 9449 DPoP. Requires strict asymmetric key binding using non-extractable client keys, verification of HTTP method and URI claims, hash-bound access tokens ('ath'), and synchronized ephemeral server-issued nonces to eliminate exfiltration and replay attacks.
globs: src/middleware/**/*.{ts,tsx}, src/auth/**/*.{ts,tsx}, src/api/**/*.{ts,tsx}, src/gateway/**/*.{ts,tsx}
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/rfc-9449-dpop-nonce-binding-standard"
---

# RFC 9449 DPoP Cryptographic Proof and Nonce Binding Standard

> **Severity**: CRITICAL | **Category**: security
> **Anti-Pattern Warning**: Unbound Bearer Token Forwarding and Missing Ephemeral DPoP Nonce Validation

## System Constraints
You are a Principal Security Architect enforcing RFC 9449 Demonstrating Proof-of-Possession (DPoP) across all edge gateways and authenticated APIs. You mandate that mutation payloads, access token issuance, and resource calls strictly bind client-held asymmetric key pairs to ephemeral server nonces and target request vectors. Reject legacy bearer tokens on protected endpoints and ensure client private keys remain strictly non-extractable.

## Strict Guidelines
- Services MUST validate the DPoP proof JWT header contains 'typ': 'dpop+jwt' and an embedded public key ('jwk') without private components.
- Services MUST verify the DPoP claims: 'htm' matches the exact HTTP method, 'htu' matches the normalized target URI (excluding query and fragment), and 'iat' is within acceptable clock skew (max 60s).
- Resource servers MUST enforce ephemeral server-generated nonces via the 'DPoP-Nonce' header, rejecting proofs with stale or missing nonces with HTTP 401 'use_dpop_nonce'.
- Resource servers MUST compute the base64url-encoded SHA-256 hash of the presented access token and match it against the DPoP proof's 'ath' claim when authenticating token-bound requests.
- Edge gateways MUST track and enforce replay protection for DPoP proof identifiers ('jti') within the token's validity window using distributed cache or atomic KV stores.
- Client runtimes MUST generate asymmetric key pairs (e.g., ECDSA with P-256 / ES256) marked as non-extractable (extractable: false) in the Web Crypto API or hardware keystore.
- Engineers MUST NOT accept static Bearer Authorization tokens on mutating endpoints configured for DPoP protection.

## Bad vs Good Implementations
### RFC 9449 DPoP Edge Validation Middleware with Nonce Binding

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Accepts standard Bearer tokens without cryptographic possession verification. If an adversary exfiltrates the access token via XSS or network eavesdropping, they can execute arbitrary mutations without proof of client key possession.
```typescript
import { Request, Response, NextFunction } from 'express';

// ANTI-PATTERN: Blindly trusting bearer tokens without cryptographic proof
// or ephemeral server nonce binding, enabling token theft and replay attacks.
export async function authenticateRequest(req: Request, res: Response, next: NextFunction) {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'Missing bearer token' });
  }

  const token = authHeader.substring(7);
  // Vulnerable: token can be exfiltrated and replayed from any client/origin
  const user = await verifyJwt(token);
  req.user = user;
  next();
}
```

#### Production Standard (Correct)
> **Standard**: Implements full RFC 9449 server-side verification: enforces 'dpop+jwt' header type, parses public JWK without private material, matches HTTP method ('htm') and clean URI ('htu'), validates ephemeral server nonces via challenge-response, checks replay protection via 'jti', and binds the access token via 'ath' SHA-256 digest.
```typescript
import { Request, Response, NextFunction } from 'express';
import { importJWK, jwtVerify } from 'jose';
import { createHash } from 'node:crypto';

interface DPoPPayload {
  htm: string;
  htu: string;
  iat: number;
  jti: string;
  nonce?: string;
  ath?: string;
}

export async function verifyDPoPProof(
  req: Request,
  res: Response,
  next: NextFunction,
  nonceStore: { validateAndRotate: (nonce: string) => Promise<boolean>; getLatest: () => Promise<string> },
  jtiStore: { hasSeen: (jti: string) => Promise<boolean>; markSeen: (jti: string, ttl: number) => Promise<void> }
) {
  const dpopHeader = req.headers['dpop'] as string | undefined;
  const authHeader = req.headers['authorization'] as string | undefined;

  if (!dpopHeader || !authHeader?.startsWith('DPoP ')) {
    res.setHeader('DPoP-Nonce', await nonceStore.getLatest());
    return res.status(401).json({ error: 'invalid_dpop_proof', error_description: 'DPoP header required' });
  }

  const rawAccessToken = authHeader.substring(5);

  try {
    // Decode unverified header to extract client's ephemeral public key
    const [headerB64] = dpopHeader.split('.');
    const header = JSON.parse(Buffer.from(headerB64, 'base64url').toString('utf8'));

    if (header.typ !== 'dpop+jwt' || !header.jwk || header.jwk.d) {
      return res.status(400).json({ error: 'invalid_dpop_proof', error_description: 'Invalid header or private key leaked' });
    }

    const clientPublicKey = await importJWK(header.jwk, header.alg);
    const { payload } = await jwtVerify(dpopHeader, clientPublicKey, {
      algorithms: ['ES256', 'EdDSA']
    }) as { payload: DPoPPayload };

    // Verify HTTP method and URI normalization (RFC 9449 Section 4.3)
    const expectedUri = `${req.protocol}://${req.get('host')}${req.baseUrl}${req.path}`;
    if (payload.htm !== req.method || payload.htu !== expectedUri) {
      return res.status(400).json({ error: 'invalid_dpop_proof', error_description: 'htm/htu mismatch' });
    }

    // Enforce Ephemeral Nonce Binding (RFC 9449 Section 8)
    if (!payload.nonce || !(await nonceStore.validateAndRotate(payload.nonce))) {
      res.setHeader('DPoP-Nonce', await nonceStore.getLatest());
      return res.status(401).json({ error: 'use_dpop_nonce', error_description: 'Invalid or expired DPoP nonce' });
    }

    // Replay defense using jti
    if (await jtiStore.hasSeen(payload.jti)) {
      return res.status(401).json({ error: 'invalid_dpop_proof', error_description: 'jti replay detected' });
    }
    await jtiStore.markSeen(payload.jti, 60);

    // Hash binding: verify 'ath' matches the access token
    const computedAth = createHash('sha256').update(rawAccessToken).digest('base64url');
    if (payload.ath !== computedAth) {
      return res.status(400).json({ error: 'invalid_dpop_proof', error_description: 'Access token hash mismatch' });
    }

    next();
  } catch (err) {
    res.setHeader('DPoP-Nonce', await nonceStore.getLatest());
    return res.status(401).json({ error: 'invalid_dpop_proof', error_description: 'Signature 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: RFC 9449 DPoP Cryptographic Proof and Nonce Binding Standard

RFC 9449 DPoP Cryptographic Proof and Nonce Binding Standard defines an industrial-grade behavioral standard for security engineering. Enforces application-layer proof-of-possession for OAuth 2.0 access tokens and mutation endpoints via RFC 9449 DPoP. Requires strict asymmetric key binding using non-extractable client keys, verification of HTTP method and URI claims, hash-bound access tokens ('ath'), and synchronized ephemeral server-issued nonces to eliminate exfiltration and replay attacks. 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: Unbound Bearer Token Forwarding and Missing Ephemeral DPoP Nonce 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 DriftUnbound Bearer Token Forwarding and Missing Ephemeral DPoP Nonce ValidationCritical
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 validate the DPoP proof JWT header contains 'typ'

Services MUST validate the DPoP proof JWT header contains 'typ': 'dpop+jwt' and an embedded public key ('jwk') without private components.

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 the DPoP claims

Services MUST verify the DPoP claims: 'htm' matches the exact HTTP method, 'htu' matches the normalized target URI (excluding query and fragment), and 'iat' is within acceptable clock skew (max 60s).

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

Resource servers MUST enforce ephemeral server-generated nonces via the 'DPoP-Nonce' header, rejecting proofs with stale or missing nonces with HTTP 401 'use_dpop_nonce'.

Resource servers MUST enforce ephemeral server-generated nonces via the 'DPoP-Nonce' header, rejecting proofs with stale or missing nonces with HTTP 401 '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.
4

Resource servers MUST compute the base64url-encoded SHA-256 hash of the presented access token and match it against the DPoP proof's 'ath' claim when authenticating token-bound requests.

Resource servers MUST compute the base64url-encoded SHA-256 hash of the presented access token and match it against the DPoP proof's 'ath' claim when authenticating token-bound requests.

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

Edge gateways MUST track and enforce replay protection for DPoP proof identifiers ('jti') within the token's validity window using distributed cache or atomic KV stores.

Edge gateways MUST track and enforce replay protection for DPoP proof identifiers ('jti') within the token's validity window using distributed cache or atomic KV 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.
6

Client runtimes MUST generate asymmetric key pairs (e.g., ECDSA with P-256 / ES256) marked as non-extractable (extractable

Client runtimes MUST generate asymmetric key pairs (e.g., ECDSA with P-256 / ES256) marked as non-extractable (extractable: false) in the Web Crypto API or hardware keystore.

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

Engineers MUST NOT accept static Bearer Authorization tokens on mutating endpoints configured for DPoP protection.

Engineers MUST NOT accept static Bearer Authorization tokens on mutating endpoints configured for DPoP protection.

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 Cryptographic Proof and Nonce Binding Standard critical for AI coding assistants?

Enforces application-layer proof-of-possession for OAuth 2.0 access tokens and mutation endpoints via RFC 9449 DPoP. Requires strict asymmetric key binding using non-extractable client keys, verification of HTTP method and URI claims, hash-bound access tokens ('ath'), and synchronized ephemeral server-issued nonces to eliminate exfiltration and replay attacks. 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 (Unbound Bearer Token Forwarding and Missing Ephemeral DPoP Nonce 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

RFC