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

Mandatory WebAuthn L3 Conditional Mediation & Passkey Lifecycle Invariants

Enforces W3C WebAuthn Level 3 conditional mediation ('conditional UI') across all public entry points to eliminate credential stuffing and adversary-in-the-middle phishing. Mandates cryptographically binding server-side challenge-response validation, strict RP ID origin scoping, AbortController lifecycle management for autofill requests, and inspection of backup eligibility and state flags (BE/BS) for synchronized passkeys.

Prohibited Anti-Pattern: Modal-only credential requests without conditional autofill, missing AbortController cleanup, and unvalidated passkey sync backup flags.

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: Mandatory WebAuthn L3 Conditional Mediation & Passkey Lifecycle Invariants - Enforces W3C WebAuthn Level 3 conditional mediation ('conditional UI') across all public entry points to eliminate credential stuffing and adversary-in-the-middle phishing. Mandates cryptographically binding server-side challenge-response validation, strict RP ID origin scoping, AbortController lifecycle management for autofill requests, and inspection of backup eligibility and state flags (BE/BS) for synchronized passkeys.
globs: src/**/*.{ts,tsx}, app/**/*.{ts,tsx}, lib/auth/**/*.{ts,tsx}, server/auth/**/*.{ts,tsx}
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/webauthn-l3-conditional-mediation-passkey-sync"
---

# Mandatory WebAuthn L3 Conditional Mediation & Passkey Lifecycle Invariants

> **Severity**: CRITICAL | **Category**: security
> **Anti-Pattern Warning**: Modal-only credential requests without conditional autofill, missing AbortController cleanup, and unvalidated passkey sync backup flags.

## System Constraints
You are a Principal Security Architect enforcing W3C WebAuthn Level 3 specifications, Conditional Mediation (Autofill Passkeys), and Passkey Synchronization Lifecycle integrity. You must reject any implementation that triggers blocking browser modal dialogs during initial page load, omits PublicKeyCredential.isConditionalMediationAvailable() feature detection, fails to manage AbortController signals for background mediation, or ignores authenticatorData flags (UP, UV, BE, BS) during registration and assertion verification.

## Strict Guidelines
- Engineers MUST ALWAYS feature-detect conditional mediation support via PublicKeyCredential.isConditionalMediationAvailable() before passing mediation: 'conditional' to navigator.credentials.get().
- Engineers MUST ALWAYS bind an AbortController to conditional mediation requests to cancel pending requests when navigating away, unmounting UI, or switching to explicit modal assertion flows.
- Engineers MUST ALWAYS annotate the primary username/identifier input with autocomplete='username webauthn' to expose discoverable credentials into browser-native autofill drop-downs.
- Engineers MUST NOT invoke blocking modal WebAuthn requests (mediation: 'silent' or undefined) on unprompted page loads or view renders.
- Services MUST cryptographically verify server-generated challenges with single-use, cryptographically secure nonces tied to an ephemeral session with a maximum TTL of 120 seconds.
- Services MUST validate the exact expected Origin and rpId against server-side canonical configurations, preventing cross-origin credential harvesting.
- Services MUST inspect and persist the Backup Eligibility (BE) and Backup State (BS) bits from authenticatorData flags during passkey registration and authentication to track multi-device passkey synchronization status.

## Bad vs Good Implementations
### Client-Side: Conditional Mediation Autofill with Abort Lifecycle

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Fails to check conditional mediation availability, launches an intrusive browser dialog on load, lacks an AbortController causing race conditions on unmount, and fails to declare the 'webauthn' autocomplete attribute.
```typescript
import { useEffect } from 'react';

export function LoginForm() {
  useEffect(() => {
    // BAD: Immediately triggers blocking modal without conditional check or AbortSignal
    fetch('/api/auth/challenge')
      .then(res => res.json())
      .then(async ({ challenge }) => {
        const cred = await navigator.credentials.get({
          publicKey: {
            challenge: Uint8Array.from(atob(challenge), c => c.charCodeAt(0)),
            rpId: window.location.hostname,
          },
        });
        await fetch('/api/auth/verify', { method: 'POST', body: JSON.stringify(cred) });
      });
  }, []);

  return (
    <form>
      {/* BAD: Missing 'webauthn' autocomplete token */}
      <input type="text" name="username" autoComplete="username" />
      <button type="submit">Sign In</button>
    </form>
  );
}
```

#### Production Standard (Correct)
> **Standard**: Properly feature-detects conditional UI, binds an AbortController for cleanup on unmount, correctly annotates the input with `autocomplete='username webauthn'`, and safely handles AbortError.
```typescript
import { useEffect, useRef } from 'react';

export function LoginForm() {
  const abortControllerRef = useRef<AbortController | null>(null);

  useEffect(() => {
    let isMounted = true;

    async function initConditionalPasskey() {
      if (!window.PublicKeyCredential || !PublicKeyCredential.isConditionalMediationAvailable) {
        return;
      }

      const available = await PublicKeyCredential.isConditionalMediationAvailable();
      if (!available || !isMounted) return;

      abortControllerRef.current?.abort();
      const controller = new AbortController();
      abortControllerRef.current = controller;

      try {
        const res = await fetch('/api/auth/generate-challenge', { signal: controller.signal });
        const { challenge, rpId } = await res.json();

        const credential = await navigator.credentials.get({
          mediation: 'conditional',
          signal: controller.signal,
          publicKey: {
            challenge: Uint8Array.from(atob(challenge), c => c.charCodeAt(0)),
            rpId,
            userVerification: 'preferred',
          },
        });

        if (credential) {
          await fetch('/api/auth/verify-assertion', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(credential),
          });
          window.location.href = '/dashboard';
        }
      } catch (err: unknown) {
        if (err instanceof DOMException && err.name === 'AbortError') return;
        console.error('Conditional passkey flow error:', err);
      }
    }

    initConditionalPasskey();
    return () => {
      isMounted = false;
      abortControllerRef.current?.abort();
    };
  }, []);

  return (
    <form onSubmit={(e) => e.preventDefault()}>
      <input
        type="text"
        name="username"
        id="username"
        autoComplete="username webauthn"
        placeholder="Username or Passkey"
      />
      <button type="submit">Continue</button>
    </form>
  );
}
```

### Server-Side: Passkey Assertion & Sync Flag Verification (BE/BS)

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Completely skips verifying authenticatorData flags (UP, UV, BE, BS), omits origin and RP ID validation, and does not check single-use challenge expiration, opening the door to replay and impersonation attacks.
```typescript
import { Request, Response } from 'express';

export async function verifyAssertion(req: Request, res: Response) {
  const { id, rawId, response } = req.body;
  // BAD: Accepts challenge from client or neglects checking session nonce, origin, and backup flags
  const storedCredential = await db.credentials.findUnique({ where: { id } });
  if (!storedCredential) {
    return res.status(401).json({ error: 'Unrecognized credential' });
  }
  // BAD: No verification of User Present (UP), User Verified (UV), or Backup flags
  return res.json({ authenticated: true });
}
```

#### Production Standard (Correct)
> **Standard**: Validates single-use session challenge, origin, and RP ID; enforces user verification; advances the signature counter to detect cloned authenticators; and records backup eligibility and backup state (BE/BS) flags.
```typescript
import { Request, Response } from 'express';
import { verifyAuthenticationResponse } from '@simplewebauthn/server';

export async function verifyAssertion(req: Request, res: Response) {
  const expectedChallenge = req.session.currentChallenge;
  if (!expectedChallenge) {
    return res.status(400).json({ error: 'Challenge expired or missing session state' });
  }

  // Clear challenge immediately to prevent replay attacks
  req.session.currentChallenge = undefined;

  const storedCredential = await db.credentials.findUnique({
    where: { credentialId: req.body.id },
  });

  if (!storedCredential) {
    return res.status(401).json({ error: 'Credential not registered' });
  }

  const verification = await verifyAuthenticationResponse({
    response: req.body,
    expectedChallenge,
    expectedOrigin: process.env.AUTH_EXPECTED_ORIGIN!,
    expectedRPID: process.env.AUTH_RP_ID!,
    credential: {
      id: storedCredential.credentialId,
      publicKey: storedCredential.publicKey,
      counter: storedCredential.counter,
      transports: storedCredential.transports,
    },
    requireUserVerification: true,
  });

  if (!verification.verified || !verification.authenticationInfo) {
    return res.status(401).json({ error: 'Passkey verification failed' });
  }

  const { newCounter, authenticatorData } = verification.authenticationInfo;
  const flags = authenticatorData.flags;
  
  // Track Passkey Sync Lifecycle (BE = Backup Eligibility, BS = Backup State)
  const isBackedUp = flags.backupState && flags.backupEligibility;

  await db.credentials.update({
    where: { credentialId: storedCredential.credentialId },
    data: {
      counter: newCounter,
      isBackedUp,
      lastUsedAt: new Date(),
    },
  });

  req.session.userId = storedCredential.userId;
  return res.status(200).json({ success: true });
}
```

Architectural Specification & Invariant Rationale

Architectural Invariant Design: Mandatory WebAuthn L3 Conditional Mediation & Passkey Lifecycle Invariants

Mandatory WebAuthn L3 Conditional Mediation & Passkey Lifecycle Invariants defines an industrial-grade behavioral standard for security engineering. Enforces W3C WebAuthn Level 3 conditional mediation ('conditional UI') across all public entry points to eliminate credential stuffing and adversary-in-the-middle phishing. Mandates cryptographically binding server-side challenge-response validation, strict RP ID origin scoping, AbortController lifecycle management for autofill requests, and inspection of backup eligibility and state flags (BE/BS) for synchronized passkeys. 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: Modal-only credential requests without conditional autofill, missing AbortController cleanup, and unvalidated passkey sync backup flags. 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 DriftModal-only credential requests without conditional autofill, missing AbortController cleanup, and unvalidated passkey sync backup flags.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

Engineers MUST ALWAYS feature-detect conditional mediation support via PublicKeyCredential.isConditionalMediationAvailable() before passing mediation

Engineers MUST ALWAYS feature-detect conditional mediation support via PublicKeyCredential.isConditionalMediationAvailable() before passing mediation: 'conditional' to navigator.credentials.get().

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 bind an AbortController to conditional mediation requests to cancel pending requests when navigating away, unmounting UI, or switching to explicit modal assertion flows.

Engineers MUST ALWAYS bind an AbortController to conditional mediation requests to cancel pending requests when navigating away, unmounting UI, or switching to explicit modal assertion flows.

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 annotate the primary username/identifier input with autocomplete='username webauthn' to expose discoverable credentials into browser-native autofill drop-downs.

Engineers MUST ALWAYS annotate the primary username/identifier input with autocomplete='username webauthn' to expose discoverable credentials into browser-native autofill drop-downs.

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

Engineers MUST NOT invoke blocking modal WebAuthn requests (mediation

Engineers MUST NOT invoke blocking modal WebAuthn requests (mediation: 'silent' or undefined) on unprompted page loads or view renders.

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 cryptographically verify server-generated challenges with single-use, cryptographically secure nonces tied to an ephemeral session with a maximum TTL of 120 seconds.

Services MUST cryptographically verify server-generated challenges with single-use, cryptographically secure nonces tied to an ephemeral session 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.
6

Services MUST validate the exact expected Origin and rpId against server-side canonical configurations, preventing cross-origin credential harvesting.

Services MUST validate the exact expected Origin and rpId against server-side canonical configurations, preventing cross-origin credential harvesting.

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

Services MUST inspect and persist the Backup Eligibility (BE) and Backup State (BS) bits from authenticatorData flags during passkey registration and authentication to track multi-device passkey synchronization status.

Services MUST inspect and persist the Backup Eligibility (BE) and Backup State (BS) bits from authenticatorData flags during passkey registration and authentication to track multi-device passkey synchronization status.

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 Mandatory WebAuthn L3 Conditional Mediation & Passkey Lifecycle Invariants critical for AI coding assistants?

Enforces W3C WebAuthn Level 3 conditional mediation ('conditional UI') across all public entry points to eliminate credential stuffing and adversary-in-the-middle phishing. Mandates cryptographically binding server-side challenge-response validation, strict RP ID origin scoping, AbortController lifecycle management for autofill requests, and inspection of backup eligibility and state flags (BE/BS) for synchronized passkeys. 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 (Modal-only credential requests without conditional autofill, missing AbortController cleanup, and unvalidated passkey sync backup flags.) 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

Mandatory