This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
securitycritical PriorityID: webauthn-l3-passkey-attestation-downgrade-guardrail

WebAuthn L3 Passkey Attestation & Credential Relay Downgrade Prevention

Enforces strict W3C WebAuthn Level 3 server-side validation invariants, covering exact origin binding, RP ID validation, cryptographically random challenge lifecycles, and user-verification enforcement. Mandates synced-credential (backup state) inspection, attestation trust path validation, and counter-rollback detection to neutralize credential relay attacks and unauthorized fallback downgrades.

Prohibited Anti-Pattern: Permissive Origin Parsing and Missing Authenticator Flags Permitting Man-in-the-Middle Relay and Downgrade

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: WebAuthn L3 Passkey Attestation & Credential Relay Downgrade Prevention - Enforces strict W3C WebAuthn Level 3 server-side validation invariants, covering exact origin binding, RP ID validation, cryptographically random challenge lifecycles, and user-verification enforcement. Mandates synced-credential (backup state) inspection, attestation trust path validation, and counter-rollback detection to neutralize credential relay attacks and unauthorized fallback downgrades.
globs: src/**/auth/**/*.{ts,tsx}, src/**/webauthn/**/*.{ts,tsx}, src/**/controllers/passkey*.{ts,tsx}, src/**/services/webauthn*.{ts,tsx}
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/webauthn-l3-passkey-attestation-downgrade-guardrail"
---

# WebAuthn L3 Passkey Attestation & Credential Relay Downgrade Prevention

> **Severity**: CRITICAL | **Category**: security
> **Anti-Pattern Warning**: Permissive Origin Parsing and Missing Authenticator Flags Permitting Man-in-the-Middle Relay and Downgrade

## System Constraints
You are a Principal Security Architect enforcing strict W3C WebAuthn Level 3 specifications and FIDO2 enterprise invariants. Never allow naive or regex-based origin comparisons, unchecked clientDataJSON challenge buffers, optional userVerification flags on step-up transactions, or silent fallback from non-discoverable to phishable credentials. Enforce cryptographic binding between the session token, challenge, RP ID, and clientDataHash to stop credential relay attacks.

## Strict Guidelines
- Services MUST validate clientDataJSON.origin using strict constant-time string equality against a strict allowlist of fully qualified domain names (including scheme and port); regex, substring, or wildcards are strictly prohibited.
- Services MUST enforce that the parsed challenge matches the cryptographically bound challenge stored in an ephemeral, single-use, TTL-bounded server-side cache (e.g., Redis) and consume it immediately upon the first verification attempt.
- Engineers MUST ALWAYS verify that flags.userPresent is true, and for high-assurance or step-up flows, require flags.userVerified to be true rather than 'preferred' or ignored.
- Services MUST verify signCount progression for device-bound hardware authenticators (signCount > storedSignCount) and reject zero-regression anomalies; for multi-device synced passkeys (backupState = true), signCount clone detection heuristics MUST be logged and correlated with device/platform metadata.
- Services MUST validate the SHA-256 hash of expected rpId against the rpIdHash within the authenticatorData payload to prevent cross-origin impersonation and relay attacks.
- Components NEVER fall back to legacy SMS, unauthenticated email OTP, or phishable factors if a passkey challenge fails or is canceled; auth flows MUST require explicit administrative re-enrollment or cryptographically attested out-of-band recovery.

## Bad vs Good Implementations
### WebAuthn L3 Assertion Verification with Strict Origin, User Verification, and Relay Checks

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Accepts clientData.origin using includes(), allowing attacker domains like 'evil-example.com'. Trust client-supplied challenge without single-use cache invalidation, ignores User Presence (UP) and User Verification (UV) flags in authenticatorData, and omits replay/relay signCount validation.
```typescript
import { Request, Response } from 'express';

export async function verifyAssertionBad(req: Request, res: Response) {
  const { credential, expectedChallenge } = req.body;
  const clientData = JSON.parse(Buffer.from(credential.response.clientDataJSON, 'base64url').toString('utf-8'));

  // VULNERABLE: Substring/loose origin check allows attacker-controlled origins
  if (!clientData.origin.includes('example.com')) {
    return res.status(400).send('Invalid origin');
  }

  // VULNERABLE: Challenge retrieved from client payload instead of secure single-use server state
  if (clientData.challenge !== expectedChallenge) {
    return res.status(400).send('Invalid challenge');
  }

  // VULNERABLE: Does not verify authenticatorData flags (UP/UV) or signCount rollback
  return res.json({ status: 'authenticated' });
}
```

#### Production Standard (Correct)
> **Standard**: Implements single-use challenge consumption, strict exact-origin verification without regex, requires explicit userVerification (UV) flag validation, and updates the hardware authenticator signature counter to detect cloned or replayed authenticators.
```typescript
import crypto from 'node:crypto';
import { verifyAuthenticationResponse } from '@simplewebauthn/server';
import type { AuthenticationResponseJSON } from '@simplewebauthn/types';

interface AuthenticatorSession {
  currentChallenge: string;
  userId: string;
  expectedOrigin: string;
  expectedRPID: string;
}

export async function verifyAssertionSecure(
  response: AuthenticationResponseJSON,
  session: AuthenticatorSession,
  storedCredential: { publicKey: Uint8Array; counter: number; transports?: string[] },
  invalidateChallenge: (challenge: string) => Promise<boolean>
) {
  // Invariant 1: Single-use challenge consumption to stop replay
  const consumed = await invalidateChallenge(session.currentChallenge);
  if (!consumed) {
    throw new SecurityError('Challenge already consumed or expired');
  }

  // Invariant 2: Cryptographic assertion verification per WebAuthn L3
  const verification = await verifyAuthenticationResponse({
    response,
    expectedChallenge: session.currentChallenge,
    expectedOrigin: session.expectedOrigin, // Strict scheme, domain, and port matching
    expectedRPID: session.expectedRPID,
    authenticator: {
      credentialPublicKey: storedCredential.publicKey,
      credentialID: response.id,
      counter: storedCredential.counter,
      transports: storedCredential.transports as any,
    },
    requireUserVerification: true, // Downgrade prevention: mandates UV bit check
  });

  if (!verification.verified || !verification.authenticationInfo) {
    throw new SecurityError('Assertion verification failed');
  }

  const { newCounter, userVerified } = verification.authenticationInfo;

  // Invariant 3: Mandate user verified flag explicitly
  if (!userVerified) {
    throw new SecurityError('UV (User Verification) flag missing; potential relay or downgrade');
  }

  return {
    success: true,
    updatedCounter: newCounter,
  };
}

class SecurityError extends Error {
  constructor(message: string) {
    super(message);
    this.name = 'SecurityError';
  }
}
```

Architectural Specification & Invariant Rationale

Architectural Invariant Design: WebAuthn L3 Passkey Attestation & Credential Relay Downgrade Prevention

WebAuthn L3 Passkey Attestation & Credential Relay Downgrade Prevention defines an industrial-grade behavioral standard for security engineering. Enforces strict W3C WebAuthn Level 3 server-side validation invariants, covering exact origin binding, RP ID validation, cryptographically random challenge lifecycles, and user-verification enforcement. Mandates synced-credential (backup state) inspection, attestation trust path validation, and counter-rollback detection to neutralize credential relay attacks and unauthorized fallback downgrades. 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: Permissive Origin Parsing and Missing Authenticator Flags Permitting Man-in-the-Middle Relay and Downgrade 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 DriftPermissive Origin Parsing and Missing Authenticator Flags Permitting Man-in-the-Middle Relay and DowngradeCritical
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 clientDataJSON.origin using strict constant-time string equality against a strict allowlist of fully qualified domain names (including scheme and port)

Services MUST validate clientDataJSON.origin using strict constant-time string equality against a strict allowlist of fully qualified domain names (including scheme and port); regex, substring, or wildcards are strictly prohibited.

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 enforce that the parsed challenge matches the cryptographically bound challenge stored in an ephemeral, single-use, TTL-bounded server-side cache (e.g., Redis) and consume it immediately upon the first verification attempt.

Services MUST enforce that the parsed challenge matches the cryptographically bound challenge stored in an ephemeral, single-use, TTL-bounded server-side cache (e.g., Redis) and consume it immediately upon the first verification attempt.

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 flags.userPresent is true, and for high-assurance or step-up flows, require flags.userVerified to be true rather than 'preferred' or ignored.

Engineers MUST ALWAYS verify that flags.userPresent is true, and for high-assurance or step-up flows, require flags.userVerified to be true rather than 'preferred' or ignored.

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 signCount progression for device-bound hardware authenticators (signCount > storedSignCount) and reject zero-regression anomalies

Services MUST verify signCount progression for device-bound hardware authenticators (signCount > storedSignCount) and reject zero-regression anomalies; for multi-device synced passkeys (backupState = true), signCount clone detection heuristics MUST be logged and correlated with device/platform metadata.

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 the SHA-256 hash of expected rpId against the rpIdHash within the authenticatorData payload to prevent cross-origin impersonation and relay attacks.

Services MUST validate the SHA-256 hash of expected rpId against the rpIdHash within the authenticatorData payload to prevent cross-origin impersonation and 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 fall back to legacy SMS, unauthenticated email OTP, or phishable factors if a passkey challenge fails or is canceled

Components NEVER fall back to legacy SMS, unauthenticated email OTP, or phishable factors if a passkey challenge fails or is canceled; auth flows MUST require explicit administrative re-enrollment or cryptographically attested out-of-band recovery.

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 L3 Passkey Attestation & Credential Relay Downgrade Prevention critical for AI coding assistants?

Enforces strict W3C WebAuthn Level 3 server-side validation invariants, covering exact origin binding, RP ID validation, cryptographically random challenge lifecycles, and user-verification enforcement. Mandates synced-credential (backup state) inspection, attestation trust path validation, and counter-rollback detection to neutralize credential relay attacks and unauthorized fallback downgrades. 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 (Permissive Origin Parsing and Missing Authenticator Flags Permitting Man-in-the-Middle Relay and Downgrade) 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