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

Mandatory User Verification (UV) and Challenge Freshness in WebAuthn Step-Up Re-Authentication

Enforces strict User Verification (UV) flags and tight challenge freshness windows on WebAuthn assertion ceremonies for high-privilege operations. Prevents credential misuse and silent step-up bypasses on multi-device synced passkeys where platform autofill might satisfy User Presence (UP) without explicit biometric or PIN verification.

Prohibited Anti-Pattern: Permitting 'preferred' user verification and omitting the authData UV bit check during privileged re-authentication

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: Mandatory User Verification (UV) and Challenge Freshness in WebAuthn Step-Up Re-Authentication - Enforces strict User Verification (UV) flags and tight challenge freshness windows on WebAuthn assertion ceremonies for high-privilege operations. Prevents credential misuse and silent step-up bypasses on multi-device synced passkeys where platform autofill might satisfy User Presence (UP) without explicit biometric or PIN verification.
globs: src/auth/**/*.{ts,tsx}, src/server/auth/**/*.{ts,js}, src/api/auth/**/*.{ts,js}, **/webauthn/**/*.{ts,js}
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/webauthn-synced-passkey-reauth-uv-guardrail"
---

# Mandatory User Verification (UV) and Challenge Freshness in WebAuthn Step-Up Re-Authentication

> **Severity**: CRITICAL | **Category**: security
> **Anti-Pattern Warning**: Permitting 'preferred' user verification and omitting the authData UV bit check during privileged re-authentication

## System Constraints
You are a Principal Security Architect enforcing zero-trust invariants on WebAuthn re-authentication and step-up flows. Synced passkeys (i.e., Apple iCloud Keychain, Google Password Manager) pose unique risks if User Verification is relaxed, as passive device unlocks or autofill heuristics may return assertion responses without explicit user re-verification. You MUST enforce userVerification='required' in assertion options, verify the UV flag bit (0x04) in the authenticatorData on the server, validate challenge freshness under 60 seconds, and prevent token replay.

## Strict Guidelines
- Engineers MUST ALWAYS set `userVerification: 'required'` (never `'preferred'` or `'discouraged'`) in `PublicKeyCredentialRequestOptions` during privileged step-up ceremonies.
- Engineers MUST ALWAYS explicitly assert `verification.authenticationInfo.userVerified === true` (or check bit 2 / 0x04 of `authenticatorData.flags`) on the backend before elevating session privileges.
- Engineers MUST NOT accept assertions where only the User Presence (`UP` / 0x01) bit is set for step-up flows.
- Services MUST restrict challenge time-to-live (TTL) to a maximum of 60 seconds for step-up re-authentication and burn the challenge immediately upon first verification attempt.
- Services MUST bind the verification challenge to both the active authenticated session ID and the specific action intended to execute (e.g., `action: 'transfer_funds'`).
- Components NEVER rely solely on client-side state or frontend promises to confirm that biometric verification occurred.

## Bad vs Good Implementations
### WebAuthn Step-Up Assertion Generation and Verification

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Using userVerification: 'preferred' and requireUserVerification: false permits multi-device synced passkeys to complete authentication with User Presence (UP) alone without enforcing a biometric or PIN prompt. Additionally, the challenge has an excessively long 5-minute timeout and is not tied to a specific action.
```typescript
import { generateAuthenticationOptions, verifyAuthenticationResponse } from '@simplewebauthn/server';
import type { Request, Response } from 'express';

export async function getStepUpOptions(req: Request, res: Response) {
  // VULNERABILITY: 'preferred' allows authenticators to skip UV (biometrics/PIN)
  // Long timeout leaves replay window open
  const options = await generateAuthenticationOptions({
    timeout: 300000,
    userVerification: 'preferred',
  });
  req.session.currentChallenge = options.challenge;
  return res.json(options);
}

export async function verifyStepUp(req: Request, res: Response) {
  const { body } = req;
  const expectedChallenge = req.session.currentChallenge;

  const verification = await verifyAuthenticationResponse({
    response: body,
    expectedChallenge,
    expectedOrigin: 'https://example.com',
    expectedRPID: 'example.com',
    authenticator: req.user.authenticators[0],
    // VULNERABILITY: defaults to false if not enforced, allowing UP-only assertions
    requireUserVerification: false,
  });

  if (verification.verified) {
    // VULNERABILITY: Privilege elevated without verifying UV flag or challenge age
    req.session.isStepUpAuthenticated = true;
    return res.json({ success: true });
  }

  return res.status(400).json({ error: 'Verification failed' });
}
```

#### Production Standard (Correct)
> **Standard**: Mandates userVerification: 'required' on options generation and requireUserVerification: true on the backend. Strictly checks verification.authenticationInfo.userVerified, limits challenge lifespan to 60 seconds, burns the challenge atomically to prevent replays, and records the elevation timestamp.
```typescript
import { generateAuthenticationOptions, verifyAuthenticationResponse } from '@simplewebauthn/server';
import type { Request, Response } from 'express';

const STEP_UP_CHALLENGE_TTL_MS = 60 * 1000; // Strict 60-second freshness window

export async function getStepUpOptions(req: Request, res: Response) {
  const userPasskeys = await getUserPasskeys(req.user.id);

  const options = await generateAuthenticationOptions({
    timeout: STEP_UP_CHALLENGE_TTL_MS,
    userVerification: 'required', // INVARIANT: Forces local biometric or PIN check
    allowCredentials: userPasskeys.map(pk => ({ id: pk.credentialId, type: 'public-key' })),
    rpID: process.env.RP_ID!,
  });

  // Bind challenge to user session, timestamp, and target scope
  await cacheStepUpChallenge(req.session.id, {
    challenge: options.challenge,
    createdAt: Date.now(),
    scope: 'privilege_elevation',
  });

  return res.json(options);
}

export async function verifyStepUp(req: Request, res: Response) {
  const cached = await getAndBurnStepUpChallenge(req.session.id);
  if (!cached) {
    return res.status(401).json({ error: 'Step-up challenge missing or already consumed' });
  }

  // Enforce challenge freshness invariant
  if (Date.now() - cached.createdAt > STEP_UP_CHALLENGE_TTL_MS) {
    return res.status(401).json({ error: 'Step-up verification timed out; please retry' });
  }

  const passkey = await getPasskeyById(req.body.id, req.user.id);
  if (!passkey) {
    return res.status(404).json({ error: 'Passkey not found' });
  }

  const verification = await verifyAuthenticationResponse({
    response: req.body,
    expectedChallenge: cached.challenge,
    expectedOrigin: process.env.RP_ORIGIN!,
    expectedRPID: process.env.RP_ID!,
    authenticator: passkey,
    requireUserVerification: true, // INVARIANT: Strictly checks UV flag (bit 0x04)
  });

  if (!verification.verified || !verification.authenticationInfo.userVerified) {
    return res.status(403).json({ error: 'User verification failed or not asserted' });
  }

  // Grant privilege bound to current time and short lifespan
  req.session.stepUpGrantedAt = Date.now();
  return res.json({ success: true });
}
```

Architectural Specification & Invariant Rationale

Architectural Invariant Design: Mandatory User Verification (UV) and Challenge Freshness in WebAuthn Step-Up Re-Authentication

Mandatory User Verification (UV) and Challenge Freshness in WebAuthn Step-Up Re-Authentication defines an industrial-grade behavioral standard for security engineering. Enforces strict User Verification (UV) flags and tight challenge freshness windows on WebAuthn assertion ceremonies for high-privilege operations. Prevents credential misuse and silent step-up bypasses on multi-device synced passkeys where platform autofill might satisfy User Presence (UP) without explicit biometric or PIN verification. 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: Permitting 'preferred' user verification and omitting the authData UV bit check during privileged re-authentication 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 DriftPermitting 'preferred' user verification and omitting the authData UV bit check during privileged re-authenticationCritical
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 set `userVerification

Engineers MUST ALWAYS set `userVerification: 'required'` (never `'preferred'` or `'discouraged'`) in `PublicKeyCredentialRequestOptions` during privileged step-up ceremonies.

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 explicitly assert `verification.authenticationInfo.userVerified === true` (or check bit 2 / 0x04 of `authenticatorData.flags`) on the backend before elevating session privileges.

Engineers MUST ALWAYS explicitly assert `verification.authenticationInfo.userVerified === true` (or check bit 2 / 0x04 of `authenticatorData.flags`) on the backend before elevating session privileges.

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 assertions where only the User Presence (`UP` / 0x01) bit is set for step-up flows.

Engineers MUST NOT accept assertions where only the User Presence (`UP` / 0x01) bit is set for step-up 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.
4

Services MUST restrict challenge time-to-live (TTL) to a maximum of 60 seconds for step-up re-authentication and burn the challenge immediately upon first verification attempt.

Services MUST restrict challenge time-to-live (TTL) to a maximum of 60 seconds for step-up re-authentication and burn the challenge immediately upon 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.
5

Services MUST bind the verification challenge to both the active authenticated session ID and the specific action intended to execute (e.g., `action

Services MUST bind the verification challenge to both the active authenticated session ID and the specific action intended to execute (e.g., `action: 'transfer_funds'`).

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 rely solely on client-side state or frontend promises to confirm that biometric verification occurred.

Components NEVER rely solely on client-side state or frontend promises to confirm that biometric verification occurred.

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 User Verification (UV) and Challenge Freshness in WebAuthn Step-Up Re-Authentication critical for AI coding assistants?

Enforces strict User Verification (UV) flags and tight challenge freshness windows on WebAuthn assertion ceremonies for high-privilege operations. Prevents credential misuse and silent step-up bypasses on multi-device synced passkeys where platform autofill might satisfy User Presence (UP) without explicit biometric or PIN verification. 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 (Permitting 'preferred' user verification and omitting the authData UV bit check during privileged re-authentication) 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