This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
securitycritical PriorityID: webauthn-l3-origin-rpid-cross-verification

WebAuthn Level 3 Cryptographic Origin, rpId, and Hash Cross-Checking Standard

Enforces strict server-side cryptographic verification of WebAuthn Level 3 clientDataJSON and authenticatorData structures. Mandates canonical origin matching, rpIdHash validation over multi-tenant top-level domains, and exact clientDataHash integrity checks to eradicate adversary-in-the-middle (AiTM) proxy interception and replay phishing attacks.

Prohibited Anti-Pattern: Trusting Client-Supplied Origins and Permissive rpId Wildcard Matching in WebAuthn Assertion Pipelines

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: WebAuthn Level 3 Cryptographic Origin, rpId, and Hash Cross-Checking Standard - Enforces strict server-side cryptographic verification of WebAuthn Level 3 clientDataJSON and authenticatorData structures. Mandates canonical origin matching, rpIdHash validation over multi-tenant top-level domains, and exact clientDataHash integrity checks to eradicate adversary-in-the-middle (AiTM) proxy interception and replay phishing attacks.
globs: src/**/auth/**/*.{ts,js}, src/**/webauthn/**/*.{ts,js}, src/**/passkey/**/*.{ts,js}, src/**/services/**/*.{ts,js}
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/webauthn-l3-origin-rpid-cross-verification"
---

# WebAuthn Level 3 Cryptographic Origin, rpId, and Hash Cross-Checking Standard

> **Severity**: CRITICAL | **Category**: security
> **Anti-Pattern Warning**: Trusting Client-Supplied Origins and Permissive rpId Wildcard Matching in WebAuthn Assertion Pipelines

## System Constraints
You are a Principal Security Architect enforcing FIDO Alliance WebAuthn Level 3 conformance. You must guarantee that every WebAuthn registration and authentication response parses and cryptographically binds clientDataJSON, origin, and rpIdHash against authoritative tenant-scoped state without relying on client-supplied headers or lax string matching.

## Strict Guidelines
- Engineers MUST ALWAYS compute the SHA-256 hash of clientDataJSON directly on the server to derive clientDataHash for signature verification.
- Engineers MUST ALWAYS validate that parsed clientDataJSON.origin exactly matches the authoritative scheme, host, and port stored in the tenant context or validated against an exact allowlist.
- Engineers MUST NOT accept wildcard origins, regex-based partial domain suffixes, or rely on reverse proxy headers like 'X-Forwarded-Host' without cryptographically verified allowlists.
- Services MUST verify that authenticatorData.rpIdHash strictly equals crypto.createHash('sha256').update(expectedRpId).digest() using constant-time comparison.
- Components NEVER proceed with signature verification (authData + clientDataHash) if clientDataJSON.type does not exactly match 'webauthn.get' for assertions or 'webauthn.create' for registrations.

## Bad vs Good Implementations
### Strict Multi-Tenant Origin and rpIdHash Cryptographic Verification

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: The bad example utilizes a loose substring search on clientData.origin (allowing bypasses like attacker-domain.com?tenant.com), completely skips validating the SHA-256 rpIdHash embedded in authenticatorData, and fails to check constant-time digests.
```typescript
import crypto from 'node:crypto';

interface AssertionInput {
  clientDataJSON: string; // base64url
  authenticatorData: string; // base64url
  signature: string; // base64url
}

export async function verifyAssertionLax(
  input: AssertionInput,
  tenantHost: string
): Promise<boolean> {
  const clientDataRaw = Buffer.from(input.clientDataJSON, 'base64url').toString('utf8');
  const clientData = JSON.parse(clientDataRaw);

  // CRITICAL FLAW: Permissive regex or substring check on origin allows phishing subdomains (e.g., evil-tenant.com)
  if (!clientData.origin.includes(tenantHost)) {
    throw new Error('Invalid origin');
  }

  // CRITICAL FLAW: Omits rpIdHash cross-verification against authData and relies on client claim
  if (clientData.type !== 'webauthn.get') {
    throw new Error('Invalid type');
  }

  return true; // Skips constant-time rpIdHash verification
}
```

#### Production Standard (Correct)
> **Standard**: The good example computes cryptographic SHA-256 clientDataHash over raw bytes, enforces strict URL origin equality, parses the first 32 bytes of authenticatorData to extract rpIdHash, and executes a constant-time comparison against the authoritative rpId hash.
```typescript
import crypto from 'node:crypto';

interface AssertionInput {
  clientDataJSON: string; // base64url
  authenticatorData: string; // base64url
}

interface TenantConfig {
  expectedOrigin: string; // Fully qualified origin: e.g., 'https://app.tenant.com'
  expectedRpId: string;   // e.g., 'tenant.com'
}

export function verifyWebAuthnOriginAndRpId(
  input: AssertionInput,
  config: TenantConfig
): { clientDataHash: Buffer; authDataBuffer: Buffer } {
  const clientDataBuffer = Buffer.from(input.clientDataJSON, 'base64url');
  const clientData = JSON.parse(clientDataBuffer.toString('utf8'));

  if (clientData.type !== 'webauthn.get') {
    throw new Error('Invalid WebAuthn operation type');
  }

  // Exact origin binding: scheme + host + port match authoritative configuration
  const parsedExpected = new URL(config.expectedOrigin);
  const parsedClientOrigin = new URL(clientData.origin);

  if (parsedClientOrigin.origin !== parsedExpected.origin) {
    throw new Error(`Origin mismatch: expected ${parsedExpected.origin}, got ${parsedClientOrigin.origin}`);
  }

  const authDataBuffer = Buffer.from(input.authenticatorData, 'base64url');
  if (authDataBuffer.length < 37) {
    throw new Error('Authenticator data truncated: minimum 37 bytes required');
  }

  // Extract first 32 bytes representing rpIdHash
  const actualRpIdHash = authDataBuffer.subarray(0, 32);
  const expectedRpIdHash = crypto.createHash('sha256').update(config.expectedRpId).digest();

  // Constant-time comparison prevents timing attacks
  if (!crypto.timingSafeEqual(actualRpIdHash, expectedRpIdHash)) {
    throw new Error('rpIdHash mismatch: authenticator signed for an unexpected relying party ID');
  }

  const clientDataHash = crypto.createHash('sha256').update(clientDataBuffer).digest();

  return { clientDataHash, authDataBuffer };
}
```

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: WebAuthn Level 3 Cryptographic Origin, rpId, and Hash Cross-Checking Standard

WebAuthn Level 3 Cryptographic Origin, rpId, and Hash Cross-Checking Standard defines an industrial-grade behavioral standard for security engineering. Enforces strict server-side cryptographic verification of WebAuthn Level 3 clientDataJSON and authenticatorData structures. Mandates canonical origin matching, rpIdHash validation over multi-tenant top-level domains, and exact clientDataHash integrity checks to eradicate adversary-in-the-middle (AiTM) proxy interception and replay phishing 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: Trusting Client-Supplied Origins and Permissive rpId Wildcard Matching in WebAuthn Assertion Pipelines 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 DriftTrusting Client-Supplied Origins and Permissive rpId Wildcard Matching in WebAuthn Assertion PipelinesCritical
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 compute the SHA-256 hash of clientDataJSON directly on the server to derive clientDataHash for signature verification.

Engineers MUST ALWAYS compute the SHA-256 hash of clientDataJSON directly on the server to derive clientDataHash for signature verification.

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 that parsed clientDataJSON.origin exactly matches the authoritative scheme, host, and port stored in the tenant context or validated against an exact allowlist.

Engineers MUST ALWAYS validate that parsed clientDataJSON.origin exactly matches the authoritative scheme, host, and port stored in the tenant context or validated against an exact allowlist.

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 wildcard origins, regex-based partial domain suffixes, or rely on reverse proxy headers like 'X-Forwarded-Host' without cryptographically verified allowlists.

Engineers MUST NOT accept wildcard origins, regex-based partial domain suffixes, or rely on reverse proxy headers like 'X-Forwarded-Host' without cryptographically verified allowlists.

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 that authenticatorData.rpIdHash strictly equals crypto.createHash('sha256').update(expectedRpId).digest() using constant-time comparison.

Services MUST verify that authenticatorData.rpIdHash strictly equals crypto.createHash('sha256').update(expectedRpId).digest() using constant-time comparison.

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

Components NEVER proceed with signature verification (authData + clientDataHash) if clientDataJSON.type does not exactly match 'webauthn.get' for assertions or 'webauthn.create' for registrations.

Components NEVER proceed with signature verification (authData + clientDataHash) if clientDataJSON.type does not exactly match 'webauthn.get' for assertions or 'webauthn.create' for registrations.

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 WebAuthn Level 3 Cryptographic Origin, rpId, and Hash Cross-Checking Standard critical for AI coding assistants?

Enforces strict server-side cryptographic verification of WebAuthn Level 3 clientDataJSON and authenticatorData structures. Mandates canonical origin matching, rpIdHash validation over multi-tenant top-level domains, and exact clientDataHash integrity checks to eradicate adversary-in-the-middle (AiTM) proxy interception and replay phishing 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 (Trusting Client-Supplied Origins and Permissive rpId Wildcard Matching in WebAuthn Assertion Pipelines) 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

WebAuthn