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

WebAuthn Level 3 Conditional Mediation Auto-Upgrade & Transactional Challenge Binding

Enforces strict WebAuthn Level 3 specifications for conditional UI auto-upgrade and cryptographically binds passkey assertion challenges to high-risk transactional session state. Mitigates AitM (Adversary-in-the-Middle) relay attacks during step-up re-authentication by disallowing cached server challenges, mandating clientDataJSON hash verification against atomic transaction contexts, and enforcing userVerification: 'required'.

Prohibited Anti-Pattern: Unbound Re-authentication Challenges and Missing Conditional UI Fallback Invariants

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: WebAuthn Level 3 Conditional Mediation Auto-Upgrade & Transactional Challenge Binding - Enforces strict WebAuthn Level 3 specifications for conditional UI auto-upgrade and cryptographically binds passkey assertion challenges to high-risk transactional session state. Mitigates AitM (Adversary-in-the-Middle) relay attacks during step-up re-authentication by disallowing cached server challenges, mandating clientDataJSON hash verification against atomic transaction contexts, and enforcing userVerification: 'required'.
globs: src/auth/**/*.{ts,tsx}, src/security/**/*.{ts,tsx}, src/server/auth/**/*.{ts,js}, src/api/auth/**/*.{ts,js}, src/hooks/usePasskey*.{ts,tsx}
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/webauthn-l3-conditional-mediation-auto-upgrade-standard"
---

# WebAuthn Level 3 Conditional Mediation Auto-Upgrade & Transactional Challenge Binding

> **Severity**: CRITICAL | **Category**: security
> **Anti-Pattern Warning**: Unbound Re-authentication Challenges and Missing Conditional UI Fallback Invariants

## System Constraints
You are a Principal Security Architect enforcing the WebAuthn Level 3 Conditional Mediation Auto-Upgrade Standard. Mandate that all passkey assertions bind transactional state to the challenge, enforce cryptographic verification of clientDataJSON, demand user verification ('required') for step-up flows, and strictly guard client-side invocation with feature detection for conditional mediation.

## Strict Guidelines
- Engineers MUST ALWAYS cryptographically bind the ephemeral challenge to a SHA-256 digest of the step-up transaction context (e.g., action, timestamp, actor ID, session nonce) to prevent challenge replay across distinct operations.
- Engineers MUST ALWAYS verify client support via PublicKeyCredential.isConditionalMediationAvailable() before passing mediation: 'conditional' to navigator.credentials.get().
- Engineers MUST NEVER configure userVerification to 'preferred' or 'discouraged' in step-up authentication flows; it MUST be explicitly set to 'required' to guarantee biometric or device-PIN verification.
- Services MUST validate that clientDataJSON.origin matches the exact server configuration and verify that the base64url-encoded challenge inside clientDataJSON matches the transaction-bound challenge issued in the same atomic session context.
- Services MUST verify signCount monotonicity and reject assertions where the counter decreases or remains zero when an authenticator previously reported an incrementing counter.
- Components NEVER persist or reuse WebAuthn challenges across multiple user actions or browser navigation boundaries; challenges MUST be strictly single-use and expire within a maximum TTL of 60 seconds.

## Bad vs Good Implementations
### Client-Side Conditional Mediation Auto-Upgrade with Transaction Challenge

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Fails to verify conditional mediation availability via PublicKeyCredential.isConditionalMediationAvailable(), which throws in unsupported browsers. Uses 'preferred' userVerification allowing biometric bypass, and fails to cryptographically bind the challenge to the specific step-up action payload.
```typescript
import { startAuthentication } from '@simplewebauthn/browser';

// ANTI-PATTERN: Blindly requesting conditional mediation without capability check,
// using static challenges, and accepting 'preferred' user verification.
export async function triggerStepUp(userId: string) {
  const res = await fetch(`/api/auth/challenge?userId=${userId}`);
  const { challenge } = await res.json();

  // Flaw: Generic challenge unbound from transaction, mediation not gated,
  // userVerification not enforced.
  const assertion = await navigator.credentials.get({
    publicKey: {
      challenge: Uint8Array.from(atob(challenge), c => c.charCodeAt(0)),
      userVerification: 'preferred',
      timeout: 60000,
    },
    mediation: 'conditional',
  });

  return fetch('/api/auth/verify', {
    method: 'POST',
    body: JSON.stringify(assertion),
  });
}
```

#### Production Standard (Correct)
> **Standard**: Validates browser capability using `isConditionalMediationAvailable()`, binds the challenge generation to a deterministic transaction context, enforces `userVerification: 'required'`, and uses standard structured types for the assertion payload.
```typescript
import { startAuthentication } from '@simplewebauthn/browser';

interface StepUpOptions {
  actionId: string;
  transactionContext: Record<string, unknown>;
}

export async function triggerSecureStepUp({ actionId, transactionContext }: StepUpOptions): Promise<Response> {
  // 1. Guard against environments lacking conditional mediation support
  const isConditionalAvailable = 
    typeof window !== 'undefined' &&
    window.PublicKeyCredential &&
    typeof window.PublicKeyCredential.isConditionalMediationAvailable === 'function' &&
    await window.PublicKeyCredential.isConditionalMediationAvailable();

  // 2. Fetch challenge explicitly bound to atomic transaction intent
  const initRes = await fetch('/api/auth/step-up/initiate', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ actionId, transactionContext }),
  });
  if (!initRes.ok) throw new Error('Failed to initialize step-up transaction challenge');
  
  const authOptions = await initRes.json();

  // 3. Guarantee strictly required user verification and conditional UI fallback
  const assertion = await startAuthentication({
    optionsJSON: {
      ...authOptions,
      userVerification: 'required',
    },
    useConditionalUI: isConditionalAvailable,
  });

  // 4. Submit assertion with transaction proof for atomic validation
  return fetch('/api/auth/step-up/finalize', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ actionId, assertion }),
  });
}
```

### Server-Side Challenge Verification Bound to Transaction Digest

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: The challenge is detached from the specific sensitive transaction, allow-listing generic authentication challenges for high-value actions. `requireUserVerification` is set to false, defeating the non-repudiation and biometric security requirements of step-up authentication.
```typescript
import { verifyAuthenticationResponse } from '@simplewebauthn/server';
import type { AuthenticationResponseJSON } from '@simplewebauthn/types';

// ANTI-PATTERN: Ignoring origin integrity, transaction linkage, and signCount
export async function verifyStepUpAssertion(req: Request, session: any) {
  const { assertion } = await req.json();

  const verification = await verifyAuthenticationResponse({
    response: assertion as AuthenticationResponseJSON,
    expectedChallenge: session.currentChallenge, // Vulnerable: Generic session challenge reused across actions
    expectedOrigin: 'https://example.com',
    expectedRPID: 'example.com',
    authenticator: session.savedAuthenticator,
    requireUserVerification: false, // Critical vulnerability: UV not enforced on step-up
  });

  if (verification.verified) {
    session.isSteppedUp = true;
  }
  return verification;
}
```

#### Production Standard (Correct)
> **Standard**: Enforces constant-time validation that the payload matches the initialized transaction context, ensures `requireUserVerification: true`, and verifies that the hardware credential's `counter` is strictly monotonic to detect authenticator cloning.
```typescript
import { verifyAuthenticationResponse } from '@simplewebauthn/server';
import type { AuthenticationResponseJSON } from '@simplewebauthn/types';
import { createHash, timingSafeEqual } from 'node:crypto';

interface StepUpSessionState {
  expectedChallenge: string;
  transactionDigest: string;
  credentialId: string;
  credentialPublicKey: Uint8Array;
  counter: number;
}

export async function verifyStepUpAssertion(
  assertion: AuthenticationResponseJSON,
  actionId: string,
  actionPayload: Record<string, unknown>,
  sessionState: StepUpSessionState,
  expectedOrigin: string,
  expectedRPID: string
) {
  // 1. Verify transaction payload integrity matches initialized step-up context
  const computedPayloadDigest = createHash('sha256')
    .update(JSON.stringify({ actionId, payload: actionPayload }))
    .digest('hex');

  const digestsMatch = timingSafeEqual(
    Buffer.from(computedPayloadDigest),
    Buffer.from(sessionState.transactionDigest)
  );
  if (!digestsMatch) {
    throw new SecurityError('Transaction context mismatch: Assertion hijacked for different action');
  }

  // 2. Strict L3 verification with mandatory User Verification
  const verification = await verifyAuthenticationResponse({
    response: assertion,
    expectedChallenge: sessionState.expectedChallenge,
    expectedOrigin,
    expectedRPID,
    authenticator: {
      credentialID: sessionState.credentialId,
      credentialPublicKey: sessionState.credentialPublicKey,
      counter: sessionState.counter,
    },
    requireUserVerification: true,
  });

  if (!verification.verified) {
    throw new SecurityError('WebAuthn L3 assertion validation failed');
  }

  // 3. Strictly detect authenticator cloning via counter decrement
  const { newCounter } = verification.authenticationInfo;
  if (sessionState.counter > 0 && newCounter <= sessionState.counter) {
    throw new SecurityError('Cloned authenticator detected: Non-monotonic counter progression');
  }

  return verification;
}
```

Architectural Specification & Invariant Rationale

Architectural Invariant Design: WebAuthn Level 3 Conditional Mediation Auto-Upgrade & Transactional Challenge Binding

WebAuthn Level 3 Conditional Mediation Auto-Upgrade & Transactional Challenge Binding defines an industrial-grade behavioral standard for security engineering. Enforces strict WebAuthn Level 3 specifications for conditional UI auto-upgrade and cryptographically binds passkey assertion challenges to high-risk transactional session state. Mitigates AitM (Adversary-in-the-Middle) relay attacks during step-up re-authentication by disallowing cached server challenges, mandating clientDataJSON hash verification against atomic transaction contexts, and enforcing userVerification: 'required'. 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: Unbound Re-authentication Challenges and Missing Conditional UI Fallback Invariants 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 DriftUnbound Re-authentication Challenges and Missing Conditional UI Fallback InvariantsCritical
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 cryptographically bind the ephemeral challenge to a SHA-256 digest of the step-up transaction context (e.g., action, timestamp, actor ID, session nonce) to prevent challenge replay across distinct operations.

Engineers MUST ALWAYS cryptographically bind the ephemeral challenge to a SHA-256 digest of the step-up transaction context (e.g., action, timestamp, actor ID, session nonce) to prevent challenge replay across distinct operations.

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 client support via PublicKeyCredential.isConditionalMediationAvailable() before passing mediation

Engineers MUST ALWAYS verify client 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.
3

Engineers MUST NEVER configure userVerification to 'preferred' or 'discouraged' in step-up authentication flows

Engineers MUST NEVER configure userVerification to 'preferred' or 'discouraged' in step-up authentication flows; it MUST be explicitly set to 'required' to guarantee biometric or device-PIN 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.
4

Services MUST validate that clientDataJSON.origin matches the exact server configuration and verify that the base64url-encoded challenge inside clientDataJSON matches the transaction-bound challenge issued in the same atomic session context.

Services MUST validate that clientDataJSON.origin matches the exact server configuration and verify that the base64url-encoded challenge inside clientDataJSON matches the transaction-bound challenge issued in the same atomic session context.

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 verify signCount monotonicity and reject assertions where the counter decreases or remains zero when an authenticator previously reported an incrementing counter.

Services MUST verify signCount monotonicity and reject assertions where the counter decreases or remains zero when an authenticator previously reported an incrementing counter.

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 persist or reuse WebAuthn challenges across multiple user actions or browser navigation boundaries

Components NEVER persist or reuse WebAuthn challenges across multiple user actions or browser navigation boundaries; challenges MUST be strictly single-use and expire within a maximum TTL of 60 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.
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 Conditional Mediation Auto-Upgrade & Transactional Challenge Binding critical for AI coding assistants?

Enforces strict WebAuthn Level 3 specifications for conditional UI auto-upgrade and cryptographically binds passkey assertion challenges to high-risk transactional session state. Mitigates AitM (Adversary-in-the-Middle) relay attacks during step-up re-authentication by disallowing cached server challenges, mandating clientDataJSON hash verification against atomic transaction contexts, and enforcing userVerification: 'required'. 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 (Unbound Re-authentication Challenges and Missing Conditional UI Fallback Invariants) 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