This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
securitycritical PriorityID: ietf-sd-jwt-nonce-replay-verification-standard

IETF SD-JWT Key Binding Nonce & Digest Replay Prevention Guardrail

Verifiers processing IETF SD-JWT presentations must enforce strict validation of Key Binding JWTs (KB-JWT), matching the presentation's sd_hash against the serialized SD-JWT disclosures and verifying a high-entropy, verifier-generated nonce via atomic single-use consumption. Failing to enforce ephemeral nonce lifecycle semantics exposes claim-level cryptographic digests to cross-verifier correlation attacks and replay forgery.

Prohibited Anti-Pattern: Stateless or Permissive Key Binding Verification Allowing Replay of Disclosed Claim Digests

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: IETF SD-JWT Key Binding Nonce & Digest Replay Prevention Guardrail - Verifiers processing IETF SD-JWT presentations must enforce strict validation of Key Binding JWTs (KB-JWT), matching the presentation's sd_hash against the serialized SD-JWT disclosures and verifying a high-entropy, verifier-generated nonce via atomic single-use consumption. Failing to enforce ephemeral nonce lifecycle semantics exposes claim-level cryptographic digests to cross-verifier correlation attacks and replay forgery.
globs: src/**/*.ts, src/**/*.service.ts, src/auth/**/*.ts, src/identity/**/*.ts
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/ietf-sd-jwt-nonce-replay-verification-standard"
---

# IETF SD-JWT Key Binding Nonce & Digest Replay Prevention Guardrail

> **Severity**: CRITICAL | **Category**: security
> **Anti-Pattern Warning**: Stateless or Permissive Key Binding Verification Allowing Replay of Disclosed Claim Digests

## System Constraints
You are a Principal Security Architect enforcing the IETF SD-JWT (Selective Disclosure for JWTs) Key Binding and Nonce Verification Standard. Mandate that every verifier implementation requires, consumes, and cryptographically validates ephemeral Key Binding JWTs (KB-JWT) with atomic nonce consumption to prevent claim-level digest correlation and token replay.

## Strict Guidelines
- Engineers MUST ALWAYS enforce atomic single-use consumption of verifier-issued `nonce` values (e.g., via Redis `GETDEL` or atomic transactional store) prior to accepting an SD-JWT presentation.
- Engineers MUST ALWAYS verify that the `sd_hash` claim in the Key Binding JWT (KB-JWT) payload matches the cryptographically calculated SHA-256 digest of the presented base SD-JWT and appended disclosures.
- Engineers MUST NOT accept SD-JWT presentations lacking a Key Binding JWT when the credential's issuer payload contains a confirmation key claim (`cnf`).
- Code SHOULD enforce strict ephemeral TTLs on issued nonces (maximum 120 seconds) and reject any KB-JWT with an expired or drifting `iat` timestamp.
- Services MUST validate that the KB-JWT `aud` (audience) claim strictly matches the verifier's canonical external identifier to prevent verifier-relay attacks.
- Components NEVER log, persist, or expose unmasked disclosure salts or raw KB-JWT cryptographic signatures in application telemetry or debug traces.

## Bad vs Good Implementations
### SD-JWT Key Binding & Nonce Verification

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: The implementation verifies the nonce without atomic invalidation, permitting replay forgery within network partitions. It fails to verify the sd_hash claim against the serialized disclosures, allowing malicious actors to detach the KB-JWT and append arbitrary disclosed digests. Additionally, it skips mandatory strict audience (aud) validation.
```typescript
import { jwtVerify, decodeJwt } from 'jose';

interface PresentationPayload {
  rawSdJwt: string; // format: <issuer-jwt>~<disclosure-1>~...~<disclosure-n>~<kb-jwt>
  nonce: string;
}

export async function verifyPresentation(payload: PresentationPayload, expectedAudience: string) {
  const parts = payload.rawSdJwt.split('~');
  const kbJwt = parts[parts.length - 1];

  // VULNERABILITY 1: Nonce is verified statelessly without consumption (replay attack allowed)
  const decodedKb = decodeJwt(kbJwt);
  if (decodedKb.nonce !== payload.nonce) {
    throw new Error('Invalid nonce');
  }

  // VULNERABILITY 2: Skipping sd_hash validation allows detached KB-JWT reuse across credentials
  // VULNERABILITY 3: Lack of audience binding allows cross-verifier relay attacks
  const holderJwk = (decodeJwt(parts[0]) as any).cnf?.jwk;
  return await jwtVerify(kbJwt, holderJwk);
}
```

#### Production Standard (Correct)
> **Standard**: Enforces all IETF SD-JWT Key Binding invariants: derives and validates the SHA-256 sd_hash using constant-time comparison, cryptographically verifies the holder's KB-JWT against the issuer's cnf public key, validates strict audience matching, and guarantees atomic single-use nonce consumption via getAndDeleteNonce.
```typescript
import { createHash, timingSafeEqual } from 'crypto';
import { jwtVerify, importJWK, JWK } from 'jose';

interface VerificationContext {
  rawSdJwt: string; // format: <issuer-jwt>~<d1>~...~<dn>~<kb-jwt>
  expectedAudience: string;
  nonceStore: {
    getAndDeleteNonce: (nonce: string) => Promise<boolean>;
  };
}

export async function verifySdJwtKeyBinding(
  context: VerificationContext
): Promise<{ isValid: boolean; holderPayload: Record<string, unknown> }> {
  const separatorIndex = context.rawSdJwt.lastIndexOf('~');
  if (separatorIndex === -1) {
    throw new Error('Malformed SD-JWT: Missing disclosure separator delimiter');
  }

  const sdJwtWithoutKb = context.rawSdJwt.substring(0, separatorIndex + 1);
  const kbJwt = context.rawSdJwt.substring(separatorIndex + 1);
  if (!kbJwt) {
    throw new Error('Missing mandatory Key Binding JWT (KB-JWT)');
  }

  // Invariant 1: Derive sd_hash via SHA-256 over everything preceding the KB-JWT
  const expectedSdHash = createHash('sha256')
    .update(sdJwtWithoutKb, 'ascii')
    .digest('base64url');

  // Invariant 2: Decode issuer JWT to resolve Holder Binding Public Key from 'cnf'
  const issuerJwt = context.rawSdJwt.split('~')[0];
  const issuerClaims = JSON.parse(Buffer.from(issuerJwt.split('.')[1], 'base64url').toString('utf8'));
  const holderJwk: JWK = issuerClaims.cnf?.jwk;
  if (!holderJwk) {
    throw new Error('Missing required confirmation key (cnf.jwk) in credential');
  }

  const holderPublicKey = await importJWK(holderJwk, holderJwk.alg || 'ES256');
  
  // Invariant 3: Cryptographically verify KB-JWT signature and canonical audience
  const { payload } = await jwtVerify(kbJwt, holderPublicKey, {
    audience: context.expectedAudience,
    maxTokenAge: '120s',
    clockTolerance: '5s',
  });

  // Invariant 4: Verify sd_hash integrity using constant-time comparison
  const presentedSdHash = payload.sd_hash as string;
  if (!presentedSdHash) {
    throw new Error('KB-JWT missing required sd_hash claim');
  }

  const hashesMatch = timingSafeEqual(
    Buffer.from(expectedSdHash, 'ascii'),
    Buffer.from(presentedSdHash, 'ascii')
  );
  if (!hashesMatch) {
    throw new Error('sd_hash mismatch: Disclosure integrity validation failed');
  }

  // Invariant 5: Atomic single-use nonce consumption prevents verifier replay
  const nonceClaim = payload.nonce as string;
  if (!nonceClaim || typeof nonceClaim !== 'string') {
    throw new Error('KB-JWT missing valid nonce claim');
  }

  const wasConsumed = await context.nonceStore.getAndDeleteNonce(nonceClaim);
  if (!wasConsumed) {
    throw new Error('Replay detected or unknown verifier nonce');
  }

  return { isValid: true, holderPayload: payload };
}
```

Recommended MCP Servers (mcp-serverlist.com)

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

Recommended Agent Skills (askills.dev)

Ready-to-use executable skill functions tailored to this stack.

Architectural Specification & Invariant Rationale

Architectural Invariant Design: IETF SD-JWT Key Binding Nonce & Digest Replay Prevention Guardrail

IETF SD-JWT Key Binding Nonce & Digest Replay Prevention Guardrail defines an industrial-grade behavioral standard for security engineering. Verifiers processing IETF SD-JWT presentations must enforce strict validation of Key Binding JWTs (KB-JWT), matching the presentation's sd_hash against the serialized SD-JWT disclosures and verifying a high-entropy, verifier-generated nonce via atomic single-use consumption. Failing to enforce ephemeral nonce lifecycle semantics exposes claim-level cryptographic digests to cross-verifier correlation attacks and replay forgery. 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 Permissive Key Binding Verification Allowing Replay of Disclosed Claim Digests 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 Permissive Key Binding Verification Allowing Replay of Disclosed Claim DigestsCritical
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 atomic single-use consumption of verifier-issued `nonce` values (e.g., via Redis `GETDEL` or atomic transactional store) prior to accepting an SD-JWT presentation.

Engineers MUST ALWAYS enforce atomic single-use consumption of verifier-issued `nonce` values (e.g., via Redis `GETDEL` or atomic transactional store) prior to accepting an SD-JWT presentation.

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 verify that the `sd_hash` claim in the Key Binding JWT (KB-JWT) payload matches the cryptographically calculated SHA-256 digest of the presented base SD-JWT and appended disclosures.

Engineers MUST ALWAYS verify that the `sd_hash` claim in the Key Binding JWT (KB-JWT) payload matches the cryptographically calculated SHA-256 digest of the presented base SD-JWT and appended disclosures.

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 NOT accept SD-JWT presentations lacking a Key Binding JWT when the credential's issuer payload contains a confirmation key claim (`cnf`).

Engineers MUST NOT accept SD-JWT presentations lacking a Key Binding JWT when the credential's issuer payload contains a confirmation key claim (`cnf`).

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

Code SHOULD enforce strict ephemeral TTLs on issued nonces (maximum 120 seconds) and reject any KB-JWT with an expired or drifting `iat` timestamp.

Code SHOULD enforce strict ephemeral TTLs on issued nonces (maximum 120 seconds) and reject any KB-JWT with an expired or drifting `iat` timestamp.

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 validate that the KB-JWT `aud` (audience) claim strictly matches the verifier's canonical external identifier to prevent verifier-relay attacks.

Services MUST validate that the KB-JWT `aud` (audience) claim strictly matches the verifier's canonical external identifier to prevent verifier-relay 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

Components NEVER log, persist, or expose unmasked disclosure salts or raw KB-JWT cryptographic signatures in application telemetry or debug traces.

Components NEVER log, persist, or expose unmasked disclosure salts or raw KB-JWT cryptographic signatures in application telemetry or debug traces.

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 IETF SD-JWT Key Binding Nonce & Digest Replay Prevention Guardrail critical for AI coding assistants?

Verifiers processing IETF SD-JWT presentations must enforce strict validation of Key Binding JWTs (KB-JWT), matching the presentation's sd_hash against the serialized SD-JWT disclosures and verifying a high-entropy, verifier-generated nonce via atomic single-use consumption. Failing to enforce ephemeral nonce lifecycle semantics exposes claim-level cryptographic digests to cross-verifier correlation attacks and replay forgery. 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 Permissive Key Binding Verification Allowing Replay of Disclosed Claim Digests) 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

IETF