This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
securitycritical PriorityID: webauthn-conditional-ui-assertion-verification

WebAuthn Conditional UI Mediation, Nonce Atomicity, and Assertion Cryptographic Verification

Enforces zero-trust cryptographic validation for FIDO2/WebAuthn Passkey authentication with Conditional UI autofill. Requires atomic single-use challenge consumption to eliminate replay attacks, strict RP ID hash and origin binding, authenticatorData flag verification (User Presence and User Verification), and monotonic signature counter enforcement to detect authenticator cloning.

Prohibited Anti-Pattern: Stateless challenge verification, unverified origin/RP ID, and ignored signature counters permitting passkey replay and authenticator cloning attacks.

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: WebAuthn Conditional UI Mediation, Nonce Atomicity, and Assertion Cryptographic Verification - Enforces zero-trust cryptographic validation for FIDO2/WebAuthn Passkey authentication with Conditional UI autofill. Requires atomic single-use challenge consumption to eliminate replay attacks, strict RP ID hash and origin binding, authenticatorData flag verification (User Presence and User Verification), and monotonic signature counter enforcement to detect authenticator cloning.
globs: src/**/*.{ts,tsx}, api/**/*.{ts,js}, server/**/*.{ts,js}, lib/auth/**/*.{ts,js}
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/webauthn-conditional-ui-assertion-verification"
---

# WebAuthn Conditional UI Mediation, Nonce Atomicity, and Assertion Cryptographic Verification

> **Severity**: CRITICAL | **Category**: security
> **Anti-Pattern Warning**: Stateless challenge verification, unverified origin/RP ID, and ignored signature counters permitting passkey replay and authenticator cloning attacks.

## System Constraints
You are a Principal Security Architect specializing in FIDO2, WebAuthn Level 3, and Passkey specifications. Mandate rigorous client-side Conditional UI mediation management and non-negotiable server-side assertion verification: atomic challenge consumption, origin and RP ID matching, cryptographic signature validation, authData flag checks (UP/UV), and monotonic signature counter validation.

## Strict Guidelines
- Client implementations MUST verify `window.PublicKeyCredential && PublicKeyCredential.isConditionalMediationAvailable` before initiating `navigator.credentials.get({ mediation: 'conditional' })`.
- Client authentication requests MUST bind an `AbortController` signal to `navigator.credentials.get` to terminate pending autofill requests on component unmount or modal transition.
- Servers MUST generate cryptographically secure challenges (minimum 32 bytes from CSPRNG) and store them in an ephemeral, atomic single-use datastore with a maximum TTL of 120 seconds.
- Servers MUST atomically consume (delete) the challenge upon the very first retrieval attempt during verification; verification MUST fail immediately if the challenge does not exist or has expired.
- Assertion verification MUST parse and verify that `clientDataJSON.type` is strictly `'webauthn.get'` and that `clientDataJSON.origin` matches the fully qualified expected origin (including scheme, domain, and port without trailing slashes).
- Assertion verification MUST compute SHA-256 over `clientDataJSON` and verify that the SHA-256 hash of the server's expected `rpId` matches the 32-byte `rpIdHash` in `authenticatorData`.
- Servers MUST verify that the User Present (UP) bit (bit 0) is set to 1 in the `authenticatorData` flags, and if required by authentication policy, the User Verified (UV) bit (bit 2) MUST also be 1.
- Servers MUST verify that the assertion `signature` is valid over the concatenation of `authenticatorData` and `clientDataHash` using the stored public key associated with the `credentialId`.
- Servers MUST compare the `signCount` from `authenticatorData` against the database counter; if `signCount > 0` or `storedSignCount > 0`, `signCount` MUST be strictly greater than `storedSignCount`, otherwise the credential MUST be revoked as a suspected cloned authenticator.

## Bad vs Good Implementations
### Passkey Assertion Verification and Replay Challenge Defense

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: The handler fails to atomically invalidate the challenge upon retrieval, exposing the system to replay attacks within the session lifetime. It omits critical origin and rpId validation against phishing, skips User Present (UP) flag checks on authenticatorData, and ignores the signCount, failing to detect cloned authenticators.
```typescript
import { Request, Response } from 'express';
import crypto from 'node:crypto';

export async function verifyAssertion(req: Request, res: Response) {
  const { credentialId, clientDataJSON, authenticatorData, signature } = req.body;
  const user = await db.users.findUnique({ where: { id: req.session.userId } });
  const passkey = await db.passkeys.findUnique({ where: { id: credentialId } });

  // BAD: Reading challenge from session without atomic invalidation allows replay attacks
  const rawClientData = Buffer.from(clientDataJSON, 'base64url').toString('utf8');
  const parsedClientData = JSON.parse(rawClientData);

  if (parsedClientData.challenge !== req.session.currentChallenge) {
    return res.status(400).json({ error: 'Invalid challenge' });
  }
  // BAD: Does not delete req.session.currentChallenge, permitting re-submission

  // BAD: No origin validation or RP ID hash validation
  // BAD: Ignoring signCount check - enables cloned authenticators to authenticate
  const clientDataHash = crypto.createHash('sha256').update(Buffer.from(clientDataJSON, 'base64url')).digest();
  const signedData = Buffer.concat([Buffer.from(authenticatorData, 'base64url'), clientDataHash]);

  const isValid = crypto.verify(
    'sha256',
    signedData,
    passkey.publicKeyPem,
    Buffer.from(signature, 'base64url')
  );

  if (!isValid) return res.status(401).json({ error: 'Invalid signature' });
  return res.json({ success: true });
}
```

#### Production Standard (Correct)
> **Standard**: Implements full defense-in-depth WebAuthn assertion checking: atomic getdel ensures single-use nonce consumption, strict origin and RP ID validation prevents phishing and credential forwarding, authenticator flags guarantee User Presence, signCount monotonicity prevents cloned authenticators, and signatures are validated using timing-safe cryptographic verification.
```typescript
import { Request, Response } from 'express';
import crypto from 'node:crypto';

interface AuthenticatorDataFlags {
  userPresent: boolean;
  userVerified: boolean;
}

function parseAuthDataFlags(flagsByte: number): AuthenticatorDataFlags {
  return {
    userPresent: (flagsByte & 0x01) !== 0,
    userVerified: (flagsByte & 0x04) !== 0,
  };
}

export async function verifyAssertion(req: Request, res: Response) {
  const { credentialId, clientDataJSON, authenticatorData, signature, userHandle } = req.body;
  const expectedOrigin = process.env.AUTH_EXPECTED_ORIGIN!; // e.g., 'https://auth.example.com'
  const expectedRpId = process.env.AUTH_RP_ID!; // e.g., 'example.com'

  // 1. Atomic challenge consumption: get-and-delete from Redis/KV
  const challengeKey = `webauthn:challenge:${userHandle}`;
  const expectedChallenge = await redis.getdel(challengeKey);
  if (!expectedChallenge) {
    return res.status(400).json({ error: 'Challenge missing, expired, or already used' });
  }

  // 2. ClientDataJSON validation
  const rawClientData = Buffer.from(clientDataJSON, 'base64url');
  const clientData = JSON.parse(rawClientData.toString('utf8'));

  if (clientData.type !== 'webauthn.get') {
    return res.status(400).json({ error: 'Invalid assertion type' });
  }
  if (clientData.challenge !== expectedChallenge) {
    return res.status(400).json({ error: 'Challenge mismatch' });
  }
  if (clientData.origin !== expectedOrigin) {
    return res.status(400).json({ error: 'Origin mismatch' });
  }

  // 3. AuthenticatorData parsing & flag validation
  const rawAuthData = Buffer.from(authenticatorData, 'base64url');
  if (rawAuthData.length < 37) {
    return res.status(400).json({ error: 'Malformed authenticatorData' });
  }

  const rpIdHash = rawAuthData.subarray(0, 32);
  const expectedRpIdHash = crypto.createHash('sha256').update(expectedRpId).digest();
  if (!crypto.timingSafeEqual(rpIdHash, expectedRpIdHash)) {
    return res.status(400).json({ error: 'RP ID hash mismatch' });
  }

  const flags = parseAuthDataFlags(rawAuthData[32]);
  if (!flags.userPresent) {
    return res.status(400).json({ error: 'User Presence (UP) flag not set' });
  }

  // 4. Monotonic counter check for clone detection
  const signCount = rawAuthData.readUInt32BE(33);
  const passkey = await db.passkeys.findUnique({ where: { id: credentialId } });
  if (!passkey) {
    return res.status(404).json({ error: 'Passkey not found' });
  }

  if (signCount !== 0 || passkey.counter !== 0) {
    if (signCount <= passkey.counter) {
      // Rollback detected - potential cloned authenticator
      await db.passkeys.update({ where: { id: credentialId }, data: { revoked: true } });
      return res.status(403).json({ error: 'Authenticator counter rollback detected. Passkey revoked.' });
    }
  }

  // 5. Cryptographic signature verification
  const clientDataHash = crypto.createHash('sha256').update(rawClientData).digest();
  const signedPayload = Buffer.concat([rawAuthData, clientDataHash]);
  const rawSignature = Buffer.from(signature, 'base64url');

  const isVerified = crypto.verify(
    'sha256',
    signedPayload,
    passkey.publicKeyPem,
    rawSignature
  );

  if (!isVerified) {
    return res.status(401).json({ error: 'Assertion signature verification failed' });
  }

  // 6. Persist incremented counter
  await db.passkeys.update({ where: { id: credentialId }, data: { counter: signCount } });

  return res.json({ verified: true });
}
```

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 Conditional UI Mediation, Nonce Atomicity, and Assertion Cryptographic Verification

WebAuthn Conditional UI Mediation, Nonce Atomicity, and Assertion Cryptographic Verification defines an industrial-grade behavioral standard for security engineering. Enforces zero-trust cryptographic validation for FIDO2/WebAuthn Passkey authentication with Conditional UI autofill. Requires atomic single-use challenge consumption to eliminate replay attacks, strict RP ID hash and origin binding, authenticatorData flag verification (User Presence and User Verification), and monotonic signature counter enforcement to detect authenticator cloning. 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 challenge verification, unverified origin/RP ID, and ignored signature counters permitting passkey replay and authenticator cloning attacks. 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 challenge verification, unverified origin/RP ID, and ignored signature counters permitting passkey replay and authenticator cloning attacks.Critical
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

Client implementations MUST verify `window.PublicKeyCredential && PublicKeyCredential.isConditionalMediationAvailable` before initiating `navigator.credentials.get({ mediation

Client implementations MUST verify `window.PublicKeyCredential && PublicKeyCredential.isConditionalMediationAvailable` before initiating `navigator.credentials.get({ mediation: 'conditional' })`.

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

Client authentication requests MUST bind an `AbortController` signal to `navigator.credentials.get` to terminate pending autofill requests on component unmount or modal transition.

Client authentication requests MUST bind an `AbortController` signal to `navigator.credentials.get` to terminate pending autofill requests on component unmount or modal transition.

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

Servers MUST generate cryptographically secure challenges (minimum 32 bytes from CSPRNG) and store them in an ephemeral, atomic single-use datastore with a maximum TTL of 120 seconds.

Servers MUST generate cryptographically secure challenges (minimum 32 bytes from CSPRNG) and store them in an ephemeral, atomic single-use datastore with a maximum TTL of 120 seconds.

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

Servers MUST atomically consume (delete) the challenge upon the very first retrieval attempt during verification

Servers MUST atomically consume (delete) the challenge upon the very first retrieval attempt during verification; verification MUST fail immediately if the challenge does not exist or has expired.

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

Assertion verification MUST parse and verify that `clientDataJSON.type` is strictly `'webauthn.get'` and that `clientDataJSON.origin` matches the fully qualified expected origin (including scheme, domain, and port without trailing slashes).

Assertion verification MUST parse and verify that `clientDataJSON.type` is strictly `'webauthn.get'` and that `clientDataJSON.origin` matches the fully qualified expected origin (including scheme, domain, and port without trailing slashes).

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

Assertion verification MUST compute SHA-256 over `clientDataJSON` and verify that the SHA-256 hash of the server's expected `rpId` matches the 32-byte `rpIdHash` in `authenticatorData`.

Assertion verification MUST compute SHA-256 over `clientDataJSON` and verify that the SHA-256 hash of the server's expected `rpId` matches the 32-byte `rpIdHash` in `authenticatorData`.

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

Servers MUST verify that the User Present (UP) bit (bit 0) is set to 1 in the `authenticatorData` flags, and if required by authentication policy, the User Verified (UV) bit (bit 2) MUST also be 1.

Servers MUST verify that the User Present (UP) bit (bit 0) is set to 1 in the `authenticatorData` flags, and if required by authentication policy, the User Verified (UV) bit (bit 2) MUST also be 1.

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.
8

Servers MUST verify that the assertion `signature` is valid over the concatenation of `authenticatorData` and `clientDataHash` using the stored public key associated with the `credentialId`.

Servers MUST verify that the assertion `signature` is valid over the concatenation of `authenticatorData` and `clientDataHash` using the stored public key associated with the `credentialId`.

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.
9

Servers MUST compare the `signCount` from `authenticatorData` against the database counter

Servers MUST compare the `signCount` from `authenticatorData` against the database counter; if `signCount > 0` or `storedSignCount > 0`, `signCount` MUST be strictly greater than `storedSignCount`, otherwise the credential MUST be revoked as a suspected cloned authenticator.

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 Conditional UI Mediation, Nonce Atomicity, and Assertion Cryptographic Verification critical for AI coding assistants?

Enforces zero-trust cryptographic validation for FIDO2/WebAuthn Passkey authentication with Conditional UI autofill. Requires atomic single-use challenge consumption to eliminate replay attacks, strict RP ID hash and origin binding, authenticatorData flag verification (User Presence and User Verification), and monotonic signature counter enforcement to detect authenticator cloning. 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 challenge verification, unverified origin/RP ID, and ignored signature counters permitting passkey replay and authenticator cloning attacks.) 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.

Related SECURITY Rules

View all rules →
1 Rule in Stack

WebAuthn