This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
securitycritical PriorityID: webauthn-enterprise-attestation-origin-binding-invariants

WebAuthn Enterprise Attestation and Strict Origin Binding Invariants

Enforces cryptographically validated hardware attestation, exact origin matching, and monotonic counter verification across all WebAuthn and Passkey flows. Eliminates credential stuffing, phishing, and authenticator cloning by validating clientDataJSON against server-bound single-use challenges and verifying FIDO2 enterprise metadata chains.

Prohibited Anti-Pattern: Accepting 'none' attestation, loose origin regexes, or unverified authenticator counters in enterprise FIDO2 ceremonies

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: WebAuthn Enterprise Attestation and Strict Origin Binding Invariants - Enforces cryptographically validated hardware attestation, exact origin matching, and monotonic counter verification across all WebAuthn and Passkey flows. Eliminates credential stuffing, phishing, and authenticator cloning by validating clientDataJSON against server-bound single-use challenges and verifying FIDO2 enterprise metadata chains.
globs: src/auth/**/*.ts, src/services/auth/**/*.ts, packages/auth/src/**/*.ts, apps/api/src/auth/**/*.ts
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/webauthn-enterprise-attestation-origin-binding-invariants"
---

# WebAuthn Enterprise Attestation and Strict Origin Binding Invariants

> **Severity**: CRITICAL | **Category**: security
> **Anti-Pattern Warning**: Accepting 'none' attestation, loose origin regexes, or unverified authenticator counters in enterprise FIDO2 ceremonies

## System Constraints
You are a Principal Security Architect enforcing strict FIDO2/WebAuthn Enterprise Attestation and Origin Binding standards. You must ensure all registration and authentication ceremonies cryptographically verify clientDataJSON against session-bound challenges, enforce strict exact-match origin validation (scheme, domain, port), parse and validate enterprise attestation certificates (x5c) against trusted hardware AAGUID roots, and enforce monotonically increasing signature counters to prevent token duplication and replay attacks.

## Strict Guidelines
- Engineers MUST ALWAYS enforce exact string matching on clientDataJSON.origin against the configured canonical origin, rejecting regex, wildcards, and substring matches.
- Engineers MUST ALWAYS store WebAuthn challenges in secure, short-lived (<= 120s), session-bound server storage and consume them atomically upon first verification attempt.
- Engineers MUST ALWAYS verify that clientDataJSON.type matches the ceremony context ('webauthn.create' for registration, 'webauthn.get' for authentication).
- Services MUST enforce user verification ('uv') flag requirement (`flags.uv === true`) for all high-assurance enterprise authentication ceremonies, rejecting pure presence-only (`up`) assertions.
- Services MUST validate the authenticator counter (`signCount`) against the stored database value, rejecting any authentication with a counter that does not strictly exceed the previous counter unless the counter is 0.
- Engineers MUST NOT accept 'none' attestation statement formats in enterprise tiers; direct or enterprise attestation with x5c certificate validation against trusted root CAs and FIDO Alliance Metadata Service (MDS3) is required.
- Components NEVER expose raw cryptographic challenges or internal WebAuthn verification parameters directly through non-ephemeral URLs or persistent browser storage.

## Bad vs Good Implementations
### WebAuthn Registration Response Verification

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Client-supplied challenge bypasses MITM and replay protection; loose regex/includes origin checks allow phishing on attacker-controlled subdomains; attestation statement is saved unparsed without root-of-trust or AAGUID validation.
```typescript
import { Request, Response } from 'express';

export async function verifyRegistration(req: Request, res: Response) {
  const { id, rawId, response, clientChallenge } = req.body;
  
  // VULNERABLE: Challenge comes from client payload, not server session/cache
  const clientData = JSON.parse(Buffer.from(response.clientDataJSON, 'base64').toString('utf8'));
  if (clientData.challenge !== clientChallenge) {
    return res.status(400).json({ error: 'Invalid challenge' });
  }

  // VULNERABLE: Loose origin validation allows attacker-controlled subdomains
  if (!clientData.origin.includes('corp.internal')) {
    return res.status(400).json({ error: 'Untrusted origin' });
  }

  // VULNERABLE: Bypasses attestation validation; accepts any unverified authenticator
  await db.credentials.create({
    data: {
      credentialId: id,
      publicKey: response.attestationObject,
      counter: 0,
      userId: req.user.id
    }
  });

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

#### Production Standard (Correct)
> **Standard**: Enforces atomic one-time challenge retrieval via redis.getdel, strict origin and RP ID matching, explicit user verification requirement, cryptographic validation of the attestation statement, and hardware AAGUID policy enforcement against an enterprise allowlist.
```typescript
import { Request, Response } from 'express';
import { verifyRegistrationResponse } from '@simplewebauthn/server';
import { redis } from '../lib/redis';
import { db } from '../lib/db';
import { TRUSTED_AAGUIDS, EXPECTED_ORIGIN, EXPECTED_RPID } from '../constants/auth';

export async function verifyRegistration(req: Request, res: Response) {
  const sessionToken = req.cookies['session_token'];
  const challengeKey = `webauthn:challenge:${sessionToken}`;
  
  // Atomic single-use retrieval of stored challenge
  const expectedChallenge = await redis.getdel(challengeKey);
  if (!expectedChallenge) {
    return res.status(400).json({ error: 'Challenge expired or invalid ceremony session' });
  }

  const verification = await verifyRegistrationResponse({
    response: req.body,
    expectedChallenge,
    expectedOrigin: EXPECTED_ORIGIN, // e.g. 'https://auth.corp.internal:443'
    expectedRPID: EXPECTED_RPID,     // e.g. 'corp.internal'
    requireUserVerification: true,
  });

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

  const { credentialID, credentialPublicKey, counter, aaguid } = verification.registrationInfo;

  // Enforce enterprise hardware attestation by validating AAGUID against hardware allowlist
  if (!TRUSTED_AAGUIDS.has(aaguid)) {
    return res.status(403).json({ error: 'Authenticator hardware not permitted by enterprise policy' });
  }

  await db.credentials.create({
    data: {
      credentialId: Buffer.from(credentialID).toString('base64url'),
      publicKey: Buffer.from(credentialPublicKey),
      counter,
      aaguid,
      userId: req.user.id,
    },
  });

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

### Authentication Assertion Counter & Clone Detection

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Disabling counter checks by passing zero or failing to persist the updated signCount prevents the detection of cloned authenticators and replay attacks.
```typescript
import { Request, Response } from 'express';
import { verifyAuthenticationResponse } from '@simplewebauthn/server';
import { db } from '../lib/db';

export async function verifyAuth(req: Request, res: Response) {
  const credential = await db.credentials.findUnique({ where: { credentialId: req.body.id } });
  
  const verification = await verifyAuthenticationResponse({
    response: req.body,
    expectedChallenge: req.session.challenge,
    expectedOrigin: 'https://auth.corp.internal',
    expectedRPID: 'corp.internal',
    authenticator: {
      credentialPublicKey: credential.publicKey,
      credentialID: credential.credentialId,
      counter: 0 // VULNERABLE: Hardcoding counter disables clone detection
    }
  });

  // VULNERABLE: Doesn't update counter in DB, allowing replayed assertion packets
  return res.json({ verified: verification.verified });
}
```

#### Production Standard (Correct)
> **Standard**: Validates user verification flags, enforces counter incrementation, automatically revokes credentials when signCount fails monotonic incrementation (detecting cloned physical keys), and updates the new signCount state transactionally.
```typescript
import { Request, Response } from 'express';
import { verifyAuthenticationResponse } from '@simplewebauthn/server';
import { redis } from '../lib/redis';
import { db } from '../lib/db';
import { EXPECTED_ORIGIN, EXPECTED_RPID } from '../constants/auth';

export async function verifyAuth(req: Request, res: Response) {
  const sessionToken = req.cookies['session_token'];
  const expectedChallenge = await redis.getdel(`webauthn:auth_challenge:${sessionToken}`);
  
  if (!expectedChallenge) {
    return res.status(400).json({ error: 'Missing or expired challenge' });
  }

  const credential = await db.credentials.findUnique({ where: { credentialId: req.body.id } });
  if (!credential) {
    return res.status(404).json({ error: 'Credential not registered' });
  }

  const verification = await verifyAuthenticationResponse({
    response: req.body,
    expectedChallenge,
    expectedOrigin: EXPECTED_ORIGIN,
    expectedRPID: EXPECTED_RPID,
    authenticator: {
      credentialPublicKey: credential.publicKey,
      credentialID: Buffer.from(credential.credentialId, 'base64url'),
      counter: credential.counter,
    },
    requireUserVerification: true,
  });

  if (!verification.verified) {
    return res.status(401).json({ error: 'Authentication assertion invalid' });
  }

  const newCounter = verification.authenticationInfo.newCounter;

  // Strictly verify counter progression unless hardware counter is fixed at 0
  if (credential.counter > 0 && newCounter <= credential.counter) {
    await db.credentials.update({
      where: { credentialId: credential.credentialId },
      data: { isRevoked: true, revokedReason: 'POSSIBLE_CLONE_DETECTED' }
    });
    return res.status(403).json({ error: 'Authenticator cloning detected; credential revoked' });
  }

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

  return res.json({ success: 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 Enterprise Attestation and Strict Origin Binding Invariants

WebAuthn Enterprise Attestation and Strict Origin Binding Invariants defines an industrial-grade behavioral standard for security engineering. Enforces cryptographically validated hardware attestation, exact origin matching, and monotonic counter verification across all WebAuthn and Passkey flows. Eliminates credential stuffing, phishing, and authenticator cloning by validating clientDataJSON against server-bound single-use challenges and verifying FIDO2 enterprise metadata chains. 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: Accepting 'none' attestation, loose origin regexes, or unverified authenticator counters in enterprise FIDO2 ceremonies 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 DriftAccepting 'none' attestation, loose origin regexes, or unverified authenticator counters in enterprise FIDO2 ceremoniesCritical
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 enforce exact string matching on clientDataJSON.origin against the configured canonical origin, rejecting regex, wildcards, and substring matches.

Engineers MUST ALWAYS enforce exact string matching on clientDataJSON.origin against the configured canonical origin, rejecting regex, wildcards, and substring matches.

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 store WebAuthn challenges in secure, short-lived (<= 120s), session-bound server storage and consume them atomically upon first verification attempt.

Engineers MUST ALWAYS store WebAuthn challenges in secure, short-lived (<= 120s), session-bound server storage and consume them atomically 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.
3

Engineers MUST ALWAYS verify that clientDataJSON.type matches the ceremony context ('webauthn.create' for registration, 'webauthn.get' for authentication).

Engineers MUST ALWAYS verify that clientDataJSON.type matches the ceremony context ('webauthn.create' for registration, 'webauthn.get' for authentication).

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 enforce user verification ('uv') flag requirement (`flags.uv === true`) for all high-assurance enterprise authentication ceremonies, rejecting pure presence-only (`up`) assertions.

Services MUST enforce user verification ('uv') flag requirement (`flags.uv === true`) for all high-assurance enterprise authentication ceremonies, rejecting pure presence-only (`up`) assertions.

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 authenticator counter (`signCount`) against the stored database value, rejecting any authentication with a counter that does not strictly exceed the previous counter unless the counter is 0.

Services MUST validate the authenticator counter (`signCount`) against the stored database value, rejecting any authentication with a counter that does not strictly exceed the previous counter unless the counter is 0.

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

Engineers MUST NOT accept 'none' attestation statement formats in enterprise tiers

Engineers MUST NOT accept 'none' attestation statement formats in enterprise tiers; direct or enterprise attestation with x5c certificate validation against trusted root CAs and FIDO Alliance Metadata Service (MDS3) is required.

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

Components NEVER expose raw cryptographic challenges or internal WebAuthn verification parameters directly through non-ephemeral URLs or persistent browser storage.

Components NEVER expose raw cryptographic challenges or internal WebAuthn verification parameters directly through non-ephemeral URLs or persistent browser storage.

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 Enterprise Attestation and Strict Origin Binding Invariants critical for AI coding assistants?

Enforces cryptographically validated hardware attestation, exact origin matching, and monotonic counter verification across all WebAuthn and Passkey flows. Eliminates credential stuffing, phishing, and authenticator cloning by validating clientDataJSON against server-bound single-use challenges and verifying FIDO2 enterprise metadata chains. 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 (Accepting 'none' attestation, loose origin regexes, or unverified authenticator counters in enterprise FIDO2 ceremonies) 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