This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
securitycritical PriorityID: webauthn-hybrid-transport-relay-guardrail

WebAuthn Hybrid Transport Cross-Device Relay and Session-Pinning Guardrail

Enforces cryptographic client-data verification and ephemeral session-binding invariants during WebAuthn cross-device (hybrid/caBLE) authentication. Prevents adversary-in-the-middle (AiTM) QR-code passkey relay attacks by mandating strict origin pinning, server-side challenge-to-session tying, and crossOrigin flag validation.

Prohibited Anti-Pattern: Unpinned Hybrid Passkey Relay Permitting Adversary-in-the-Middle Origin Masquerading

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: WebAuthn Hybrid Transport Cross-Device Relay and Session-Pinning Guardrail - Enforces cryptographic client-data verification and ephemeral session-binding invariants during WebAuthn cross-device (hybrid/caBLE) authentication. Prevents adversary-in-the-middle (AiTM) QR-code passkey relay attacks by mandating strict origin pinning, server-side challenge-to-session tying, and crossOrigin flag validation.
globs: src/**/auth/**/*.{ts,tsx,js,mjs}, src/**/webauthn/**/*.{ts,tsx,js,mjs}, packages/**/auth/**/*.{ts,tsx,js,mjs}, server/**/api/auth/**/*.{ts,js}
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/webauthn-hybrid-transport-relay-guardrail"
---

# WebAuthn Hybrid Transport Cross-Device Relay and Session-Pinning Guardrail

> **Severity**: CRITICAL | **Category**: security
> **Anti-Pattern Warning**: Unpinned Hybrid Passkey Relay Permitting Adversary-in-the-Middle Origin Masquerading

## System Constraints
You are a Principal Security Architect specializing in FIDO2/WebAuthn protocols. Your mandate is to enforce strict cross-device relay invariants for hybrid passkey authentication flows. In hybrid transport (caBLE), an attacker can proxy the displayed QR code to an out-of-band victim. You must strictly require that all WebAuthn verification pipelines bind ephemeral challenges to the initiating browser session, enforce immutable origin equality, validate the Level 3 clientDataJSON crossOrigin property, and invalidate challenges atomically on receipt.

## Strict Guidelines
- Engineers MUST ALWAYS cryptographically bind the WebAuthn challenge to the initiating browser session via an encrypted, HttpOnly, SameSite=Strict cookie or server-side ephemeral session store.
- Engineers MUST NOT accept or verify an assertion challenge passed directly by the client payload without verifying its 1:1 match with the server session challenge.
- Engineers MUST ALWAYS perform constant-time comparison on challenges using crypto.timingSafeEqual to mitigate timing-oracle attacks.
- Engineers MUST invalidate (consume) the challenge immediately upon the first verification attempt, regardless of whether verification succeeds or fails, to prevent replay attacks.
- Code MUST strictly validate clientDataJSON.origin against the canonical, pre-configured Relying Party Origin (scheme, exact hostname, and explicit port if non-default); wildcards or client-provided origins are strictly prohibited.
- Services MUST verify the clientDataJSON.crossOrigin boolean flag where present; assertions received with crossOrigin: true MUST be rejected unless explicitly allowed by an authorized multi-domain relying party topology.
- Components NEVER expose QR code pairing states or raw hybrid transport connection strings across public, unauthenticated WebSockets without cryptographic session token authentication.
- Services MUST enforce an ephemeral challenge expiration TTL of no more than 60 seconds for cross-device hybrid flows to shrink the attack window for QR relay.

## Bad vs Good Implementations
### WebAuthn Assertion Verification with Session Pinning and Hybrid Relay Guardrails

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: The handler trusts the client-provided challenge instead of pinning it to the initiator's authenticated server-side session. Origin matching is flawed using endsWith(), leaving it vulnerable to malicious subdomain or sibling domain attacks (e.g., rogue-example.com). It fails to consume the challenge immediately, lacks timing-safe challenge comparison, and ignores the WebAuthn Level 3 crossOrigin flag.
```typescript
import { Request, Response } from 'express';

export async function verifyPasskeyAssertion(req: Request, res: Response) {
  // VULNERABLE: Challenge retrieved directly from client request body
  // Allows an attacker to relay a QR code and fulfill authentication against arbitrary victim sessions
  const { id, rawId, response, challenge: clientSuppliedChallenge } = req.body;

  const clientDataJSON = JSON.parse(
    Buffer.from(response.clientDataJSON, 'base64url').toString('utf-8')
  );

  // VULNERABLE: Origin loosely checked with endsWith (allows attacker-controlled evil-domain.com)
  if (!clientDataJSON.origin.endsWith('example.com')) {
    return res.status(400).json({ error: 'Invalid origin' });
  }

  // VULNERABLE: Replay window open; client supplies their own expected challenge
  if (clientDataJSON.challenge !== clientSuppliedChallenge) {
    return res.status(400).json({ error: 'Challenge mismatch' });
  }

  // Missing crossOrigin check and missing signCount validation
  return res.status(200).json({ verified: true });
}
```

#### Production Standard (Correct)
> **Standard**: The production implementation strictly ties the expected challenge to the initiator's server-managed session and destroys it immediately upon retrieval to prevent replays. It enforces a 60-second hybrid TTL, uses strict string equality against the configured Relying Party Origin, rejects assertions where crossOrigin is true, and verifies the challenge with timing-safe comparison.
```typescript
import { Request, Response } from 'express';
import crypto from 'node:crypto';

interface AuthenticatorSessionData {
  challenge: string;
  expiresAt: number;
  userId?: string;
}

const EXPECTED_RP_ORIGIN = 'https://auth.example.com';
const EXPECTED_RP_ID = 'example.com';
const MAX_HYBRID_TTL_MS = 60_000; // 60 seconds max for hybrid QR window

export async function verifyPasskeyAssertion(req: Request, res: Response) {
  const session = req.session as unknown as { webauthn?: AuthenticatorSessionData };
  const stored = session?.webauthn;

  // 1. Enforce active challenge in initiator's session context
  if (!stored?.challenge || !stored?.expiresAt) {
    return res.status(401).json({ error: 'No authentication challenge initialized' });
  }

  // 2. Atomic consumption: invalidate immediately before processing
  const expectedChallenge = stored.challenge;
  const expiresAt = stored.expiresAt;
  delete session.webauthn;

  // 3. Ephemeral window expiration check
  if (Date.now() > expiresAt) {
    return res.status(401).json({ error: 'Authentication challenge expired' });
  }

  const { response } = req.body;
  if (!response?.clientDataJSON || !response?.authenticatorData) {
    return res.status(400).json({ error: 'Malformed assertion payload' });
  }

  // 4. Parse clientDataJSON strictly
  let clientData: { type: string; challenge: string; origin: string; crossOrigin?: boolean };
  try {
    const rawJSON = Buffer.from(response.clientDataJSON, 'base64url').toString('utf-8');
    clientData = JSON.parse(rawJSON);
  } catch {
    return res.status(400).json({ error: 'Invalid clientDataJSON encoding' });
  }

  // 5. Enforce operation type invariant
  if (clientData.type !== 'webauthn.get') {
    return res.status(400).json({ error: 'Unexpected clientData type' });
  }

  // 6. Strict immutable origin matching (exact match invariant)
  if (clientData.origin !== EXPECTED_RP_ORIGIN) {
    return res.status(403).json({ error: 'Origin mismatch: possible relay attack' });
  }

  // 7. Enforce crossOrigin relay guardrail (Level 3 spec)
  if (clientData.crossOrigin === true) {
    return res.status(403).json({ error: 'Unauthorized cross-origin passkey assertion' });
  }

  // 8. Constant-time challenge verification
  const expectedBuffer = Buffer.from(expectedChallenge, 'base64url');
  const actualBuffer = Buffer.from(clientData.challenge, 'base64url');
  if (
    expectedBuffer.length !== actualBuffer.length ||
    !crypto.timingSafeEqual(expectedBuffer, actualBuffer)
  ) {
    return res.status(401).json({ error: 'Cryptographic challenge verification failed' });
  }

  // Proceed with FIDO2 signature & signCount verification...
  return res.status(200).json({ verified: true });
}
```

Recommended MCP Servers (mcp-serverlist.com)

Equip agent with system tools to verify code for this stack.

Architectural Specification & Invariant Rationale

Architectural Invariant Design: WebAuthn Hybrid Transport Cross-Device Relay and Session-Pinning Guardrail

WebAuthn Hybrid Transport Cross-Device Relay and Session-Pinning Guardrail defines an industrial-grade behavioral standard for security engineering. Enforces cryptographic client-data verification and ephemeral session-binding invariants during WebAuthn cross-device (hybrid/caBLE) authentication. Prevents adversary-in-the-middle (AiTM) QR-code passkey relay attacks by mandating strict origin pinning, server-side challenge-to-session tying, and crossOrigin flag validation. 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: Unpinned Hybrid Passkey Relay Permitting Adversary-in-the-Middle Origin Masquerading 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 DriftUnpinned Hybrid Passkey Relay Permitting Adversary-in-the-Middle Origin MasqueradingCritical
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 WebAuthn challenge to the initiating browser session via an encrypted, HttpOnly, SameSite=Strict cookie or server-side ephemeral session store.

Engineers MUST ALWAYS cryptographically bind the WebAuthn challenge to the initiating browser session via an encrypted, HttpOnly, SameSite=Strict cookie or server-side ephemeral session store.

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 NOT accept or verify an assertion challenge passed directly by the client payload without verifying its 1

Engineers MUST NOT accept or verify an assertion challenge passed directly by the client payload without verifying its 1:1 match with the server session challenge.

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 perform constant-time comparison on challenges using crypto.timingSafeEqual to mitigate timing-oracle attacks.

Engineers MUST ALWAYS perform constant-time comparison on challenges using crypto.timingSafeEqual to mitigate timing-oracle attacks.

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 invalidate (consume) the challenge immediately upon the first verification attempt, regardless of whether verification succeeds or fails, to prevent replay attacks.

Engineers MUST invalidate (consume) the challenge immediately upon the first verification attempt, regardless of whether verification succeeds or fails, to prevent replay attacks.

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

Code MUST strictly validate clientDataJSON.origin against the canonical, pre-configured Relying Party Origin (scheme, exact hostname, and explicit port if non-default)

Code MUST strictly validate clientDataJSON.origin against the canonical, pre-configured Relying Party Origin (scheme, exact hostname, and explicit port if non-default); wildcards or client-provided origins are strictly prohibited.

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 verify the clientDataJSON.crossOrigin boolean flag where present

Services MUST verify the clientDataJSON.crossOrigin boolean flag where present; assertions received with crossOrigin: true MUST be rejected unless explicitly allowed by an authorized multi-domain relying party topology.

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 QR code pairing states or raw hybrid transport connection strings across public, unauthenticated WebSockets without cryptographic session token authentication.

Components NEVER expose QR code pairing states or raw hybrid transport connection strings across public, unauthenticated WebSockets without cryptographic session token 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.
8

Services MUST enforce an ephemeral challenge expiration TTL of no more than 60 seconds for cross-device hybrid flows to shrink the attack window for QR relay.

Services MUST enforce an ephemeral challenge expiration TTL of no more than 60 seconds for cross-device hybrid flows to shrink the attack window for QR relay.

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 Hybrid Transport Cross-Device Relay and Session-Pinning Guardrail critical for AI coding assistants?

Enforces cryptographic client-data verification and ephemeral session-binding invariants during WebAuthn cross-device (hybrid/caBLE) authentication. Prevents adversary-in-the-middle (AiTM) QR-code passkey relay attacks by mandating strict origin pinning, server-side challenge-to-session tying, and crossOrigin flag validation. 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 (Unpinned Hybrid Passkey Relay Permitting Adversary-in-the-Middle Origin Masquerading) 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