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

RFC 9449 DPoP Sender-Constrained Token Attestation & Ephemeral Proof Validation

Mandates end-to-end cryptographic binding of OAuth 2.0 access and refresh tokens to client-held private keys via RFC 9449 DPoP proofs. Eliminates bearer token theft, exfiltration, and replay attacks by enforcing asymmetric key attestation, access token hash (ath) binding, HTTP method/URI parity, and strict single-use jti validation. Prohibits fallback to unconstrained bearer tokens and prevents DPoP proof caching across distinct HTTP requests.

Prohibited Anti-Pattern: Accepting Unbound Bearer Tokens or Reusing Static DPoP Proofs Without ath, htm/htu, and jti Verification

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: RFC 9449 DPoP Sender-Constrained Token Attestation & Ephemeral Proof Validation - Mandates end-to-end cryptographic binding of OAuth 2.0 access and refresh tokens to client-held private keys via RFC 9449 DPoP proofs. Eliminates bearer token theft, exfiltration, and replay attacks by enforcing asymmetric key attestation, access token hash (ath) binding, HTTP method/URI parity, and strict single-use jti validation. Prohibits fallback to unconstrained bearer tokens and prevents DPoP proof caching across distinct HTTP requests.
globs: src/auth/**/*.{ts,tsx}, src/middleware/**/*.{ts,tsx}, src/api/**/*.{ts,tsx}, src/security/**/*.{ts,tsx}
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/rfc-9449-dpop-ephemeral-attestation-standard"
---

# RFC 9449 DPoP Sender-Constrained Token Attestation & Ephemeral Proof Validation

> **Severity**: CRITICAL | **Category**: security
> **Anti-Pattern Warning**: Accepting Unbound Bearer Tokens or Reusing Static DPoP Proofs Without ath, htm/htu, and jti Verification

## System Constraints
You are a Principal Security Architect enforcing the RFC 9449 DPoP (Demonstrating Proof of Possession at the Application Layer) standard. Mandate strict sender-constrained token attestation. Reject all ambient bearer tokens in protected resource flows, enforce asymmetric cryptographic proof validation on every request, guarantee access token hash (ath) verification, and prevent token replay using single-use jti nonces and tight time skew boundaries.

## Strict Guidelines
- Engineers MUST ALWAYS enforce asymmetric algorithms (such as ES256, Ed25519/EdDSA, or PS256) for DPoP proofs and explicitly reject symmetric algorithms (HS256) or 'none'.
- Resource Servers MUST verify that the incoming HTTP Authorization header uses the 'DPoP' scheme (Authorization: DPoP <access_token>) instead of 'Bearer' for protected resources requiring sender constraints.
- Resource Servers MUST compute the SHA-256 base64url-encoded hash of the presented access token and assert exact equality against the 'ath' claim in the DPoP proof JWT payload.
- Resource Servers MUST strictly validate that the DPoP proof's 'htm' claim matches the HTTP request method and 'htu' matches the fully qualified request URI excluding query and fragment parts.
- Services MUST check that the proof 'iat' (issued-at) timestamp is within an ephemeral skew window (maximum +/- 60 seconds) and MUST store the 'jti' in an atomic distributed replay-cache (e.g., Redis) with an active TTL to prevent replay attacks.
- Clients MUST generate an ephemeral, single-use DPoP proof JWT for every outbound authenticated HTTP request and MUST NEVER reuse proof tokens across multiple requests.
- Resource Servers MUST emit HTTP 401 with a fresh 'DPoP-Nonce' header when a required nonce is missing or invalidated, and client agents MUST handle nonces by retrying with the server-supplied nonce exactly once.

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

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: This anti-pattern accepts ambient 'Bearer' tokens instead of enforcing 'DPoP' scheme tokens, does not cryptographically verify the DPoP public key inside the JOSE header, omits the mandatory 'ath' token-hash check, allows cross-endpoint reuse by omitting 'htm' and 'htu' checks, uses symmetric secrets, and does not record the 'jti' to prevent replay attacks.
```typescript
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';

// VULNERABLE: Reverts to ambient bearer tokens, accepts symmetric algs,
// fails to verify HTTP method/URI binding, ignores ath hash, and permits replay.
export async function authenticateResource(req: Request, res: Response, next: NextFunction) {
  const authHeader = req.headers.authorization;
  if (!authHeader?.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'Missing bearer token' });
  }

  const token = authHeader.split(' ')[1];
  const dpopProof = req.headers['dpop'] as string | undefined;

  try {
    // Insecure: DPoP is optional or checked without binding constraints
    if (dpopProof) {
      const decoded = jwt.decode(dpopProof) as any;
      if (!decoded) return res.status(401).json({ error: 'Invalid DPoP' });
      // Flaw: No jti replay store, no ath check, no htm/htu validation, no public key verification
    }

    const payload = jwt.verify(token, process.env.JWT_SECRET! /* Flaw: Shared symmetric secret */);
    req.user = payload;
    next();
  } catch (err) {
    return res.status(401).json({ error: 'Unauthorized' });
  }
}
```

#### Production Standard (Correct)
> **Standard**: This production implementation adheres strictly to RFC 9449: it mandates the 'DPoP' authorization scheme, validates the 'dpop+jwt' header and asymmetric alg, verifies the proof using the embedded JWK, validates strict matching of 'htm' and sanitized 'htu', asserts 'ath' equivalence with the SHA-256 hash of the access token, checks distributed single-use 'jti' freshness with Redis TTL, and confirms that the token's 'cnf.jkt' thumbprint matches the signing key.
```typescript
import { Request, Response, NextFunction } from 'express';
import crypto from 'crypto';
import * as jose from 'jose';
import { redisClient } from '../services/redis';

const SKEW_WINDOW_SECONDS = 60;
const ALLOWED_ALGS = ['ES256', 'PS256', 'EdDSA'];

function computeAth(accessToken: string): string {
  return crypto.createHash('sha256').update(accessToken, 'ascii').digest('base64url');
}

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

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

  if (!dpopHeader || typeof dpopHeader !== 'string') {
    res.setHeader('WWW-Authenticate', 'DPoP error="invalid_dpop_proof"');
    return res.status(401).json({ error: 'missing_dpop_proof' });
  }

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

  try {
    // 1. Parse unverified header to extract JWK and ensure alg correctness
    const { header } = jose.decodeProtectedHeader(dpopHeader) as { header: jose.ProtectedHeaderParameters & { typ?: string; jwk?: jose.JWK } };
    if (header.typ !== 'dpop+jwt' || !header.jwk || !header.alg || !ALLOWED_ALGS.includes(header.alg)) {
      return res.status(401).json({ error: 'invalid_dpop_header' });
    }

    // 2. Import ephemeral client public key embedded in the proof header
    const clientPublicKey = await jose.importJWK(header.jwk, header.alg);

    // 3. Cryptographically verify the DPoP proof against the embedded public key
    const { payload } = await jose.jwtVerify(dpopHeader, clientPublicKey, {
      algorithms: ALLOWED_ALGS,
      maxTokenAge: `${SKEW_WINDOW_SECONDS}s`,
      clockTolerance: '5s'
    });

    // 4. Validate htm (HTTP Method) and htu (HTTP URI normalized)
    const currentHtu = `${req.protocol}://${req.get('host')}${req.baseUrl}${req.path}`;
    if (payload.htm !== req.method.toUpperCase()) {
      return res.status(401).json({ error: 'invalid_htm' });
    }
    if (payload.htu !== currentHtu) {
      return res.status(401).json({ error: 'invalid_htu' });
    }

    // 5. Validate ath (SHA-256 access token hash binding)
    const expectedAth = computeAth(accessToken);
    if (!payload.ath || payload.ath !== expectedAth) {
      return res.status(401).json({ error: 'invalid_ath_binding' });
    }

    // 6. Anti-replay enforcement via atomic jti reservation in distributed cache
    if (!payload.jti || typeof payload.jti !== 'string') {
      return res.status(401).json({ error: 'missing_jti' });
    }
    const replayKey = `dpop:jti:${payload.jti}`;
    const wasSet = await redisClient.set(replayKey, '1', 'EX', SKEW_WINDOW_SECONDS * 2, 'NX');
    if (!wasSet) {
      return res.status(401).json({ error: 'dpop_proof_replayed' });
    }

    // 7. Verify the sender-constrained access token itself against Authorization Server JWKS
    // and ensure its cnf.jkt matches the SHA-256 thumbprint of header.jwk
    const clientThumbprint = await jose.calculateJwkThumbprint(header.jwk, 'sha256');
    const verifiedAccessToken = await jose.jwtVerify(accessToken, jose.createRemoteJWKSet(new URL(process.env.OIDC_JWKS_URI!)));

    const tokenCnf = verifiedAccessToken.payload.cnf as { jkt?: string } | undefined;
    if (!tokenCnf || tokenCnf.jkt !== clientThumbprint) {
      return res.status(401).json({ error: 'token_key_mismatch' });
    }

    req.user = verifiedAccessToken.payload;
    next();
  } catch (error) {
    return res.status(401).json({ error: 'dpop_attestation_failed' });
  }
}
```

Architectural Specification & Invariant Rationale

Architectural Invariant Design: RFC 9449 DPoP Sender-Constrained Token Attestation & Ephemeral Proof Validation

RFC 9449 DPoP Sender-Constrained Token Attestation & Ephemeral Proof Validation defines an industrial-grade behavioral standard for security engineering. Mandates end-to-end cryptographic binding of OAuth 2.0 access and refresh tokens to client-held private keys via RFC 9449 DPoP proofs. Eliminates bearer token theft, exfiltration, and replay attacks by enforcing asymmetric key attestation, access token hash (ath) binding, HTTP method/URI parity, and strict single-use jti validation. Prohibits fallback to unconstrained bearer tokens and prevents DPoP proof caching across distinct HTTP requests. 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 Reusing Static DPoP Proofs Without ath, htm/htu, and jti Verification 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 Reusing Static DPoP Proofs Without ath, htm/htu, and jti VerificationCritical
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 algorithms (such as ES256, Ed25519/EdDSA, or PS256) for DPoP proofs and explicitly reject symmetric algorithms (HS256) or 'none'.

Engineers MUST ALWAYS enforce asymmetric algorithms (such as ES256, Ed25519/EdDSA, or PS256) for DPoP proofs and explicitly reject symmetric algorithms (HS256) or 'none'.

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

Resource Servers MUST verify that the incoming HTTP Authorization header uses the 'DPoP' scheme (Authorization

Resource Servers MUST verify that the incoming HTTP Authorization header uses the 'DPoP' scheme (Authorization: DPoP <access_token>) instead of 'Bearer' for protected resources requiring sender constraints.

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 compute the SHA-256 base64url-encoded hash of the presented access token and assert exact equality against the 'ath' claim in the DPoP proof JWT payload.

Resource Servers MUST compute the SHA-256 base64url-encoded hash of the presented access token and assert exact equality against the 'ath' claim in the DPoP proof JWT 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.
4

Resource Servers MUST strictly validate that the DPoP proof's 'htm' claim matches the HTTP request method and 'htu' matches the fully qualified request URI excluding query and fragment parts.

Resource Servers MUST strictly validate that the DPoP proof's 'htm' claim matches the HTTP request method and 'htu' matches the fully qualified request URI excluding query and fragment parts.

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 check that the proof 'iat' (issued-at) timestamp is within an ephemeral skew window (maximum +/- 60 seconds) and MUST store the 'jti' in an atomic distributed replay-cache (e.g., Redis) with an active TTL to prevent replay attacks.

Services MUST check that the proof 'iat' (issued-at) timestamp is within an ephemeral skew window (maximum +/- 60 seconds) and MUST store the 'jti' in an atomic distributed replay-cache (e.g., Redis) with an active TTL to prevent 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.
6

Clients MUST generate an ephemeral, single-use DPoP proof JWT for every outbound authenticated HTTP request and MUST NEVER reuse proof tokens across multiple requests.

Clients MUST generate an ephemeral, single-use DPoP proof JWT for every outbound authenticated HTTP request and MUST NEVER reuse proof tokens across multiple 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.
7

Resource Servers MUST emit HTTP 401 with a fresh 'DPoP-Nonce' header when a required nonce is missing or invalidated, and client agents MUST handle nonces by retrying with the server-supplied nonce exactly once.

Resource Servers MUST emit HTTP 401 with a fresh 'DPoP-Nonce' header when a required nonce is missing or invalidated, and client agents MUST handle nonces by retrying with the server-supplied nonce exactly once.

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 Sender-Constrained Token Attestation & Ephemeral Proof Validation critical for AI coding assistants?

Mandates end-to-end cryptographic binding of OAuth 2.0 access and refresh tokens to client-held private keys via RFC 9449 DPoP proofs. Eliminates bearer token theft, exfiltration, and replay attacks by enforcing asymmetric key attestation, access token hash (ath) binding, HTTP method/URI parity, and strict single-use jti validation. Prohibits fallback to unconstrained bearer tokens and prevents DPoP proof caching across distinct HTTP requests. 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 Reusing Static DPoP Proofs Without ath, htm/htu, and jti Verification) 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