Relying Party and Authenticator Roles

The foundation of modern passwordless identity relies on a strict cryptographic separation between the Relying Party and Authenticator Roles. In the Web Authentication (WebAuthn) specification, these two entities operate across distinct trust boundaries to eliminate shared secrets, mitigate phishing, and enable secure passkey provisioning. While the browser acts as a transport mediator, the actual credential generation, storage, and signing occur exclusively within hardware-backed or OS-managed authenticators. For a comprehensive overview of the underlying standards and protocol layering, review WebAuthn & FIDO2 Protocol Fundamentals before implementing role-specific workflows.

graph TD
 Client[Browser / OS Client] -->|WebAuthn API| RP[Relying Party Server]
 Client -->|CTAP2 / Platform API| Auth[Authenticator Hardware/OS]
 Auth -->|Attestation/Assertion| Client
 RP -->|Challenge/Options| Client
 classDef rp fill:#e3f2fd,stroke:#1565c0;
 classDef auth fill:#e8f5e9,stroke:#2e7d32;
 class RP rp;
 class Auth auth;

Figure 1: Trust boundary and message flow defining the Relying Party and Authenticator Roles in WebAuthn credential operations.

1. Defining the Core Entities in Modern Authentication

The WebAuthn specification enforces a strict separation of duties. The Relying Party (RP) represents the application or service requesting authentication, while the Authenticator is the cryptographic module that generates, stores, and uses asymmetric key pairs. Understanding this division is critical when mapping responsibilities across the FIDO2 stack: the browser/OS client handles API mediation, the CTAP2 authenticator executes cryptographic operations, and the RP server backend manages state and policy.

Validation & Implementation Workflows

  • Map entity responsibilities: Align RP server logic with registration (create) and authentication (get) phases. The RP never touches private key material.
  • Validate RP ID scoping: Ensure rp.id matches the effective domain and is validated against window.location.origin. Subdomain scoping must be explicitly configured for multi-tenant deployments.
  • Confirm capability flags: Query PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable() and parse authenticatorSelection requirements before initiating credential creation.

Platform Execution Differences

Execution Context Mediation Layer Key Storage
Browser-Mediated WebAuthn JS API Abstracted via OS Credential Manager
Platform-Bound Secure Enclave / TPM Non-exportable, device-locked
Cross-Platform Roaming CTAP2 (USB/NFC/BLE) Secure Element / Smart Card

TypeScript Interface & RP ID Derivation

// Explicit type definition for PublicKeyCredentialCreationOptions
export interface WebAuthnRegistrationOptions {
 rp: {
 id: string;
 name: string;
 };
 user: {
 id: Uint8Array;
 name: string;
 displayName: string;
 };
 challenge: Uint8Array;
 pubKeyCredParams: PublicKeyCredentialParameters[];
 authenticatorSelection?: AuthenticatorSelectionCriteria;
 attestation?: AttestationConveyancePreference;
 timeout?: number;
}

// Derive RP ID safely from current origin
export function deriveRpId(): string {
 const origin = window.location.origin;
 const url = new URL(origin);
 // Strip www. and subdomains per WebAuthn spec effective domain rules
 const parts = url.hostname.split('.');
 return parts.length > 2 ? parts.slice(-2).join('.') : url.hostname;
}

️ Implementation Pitfalls**

  • Conflating the client browser with the authenticator hardware leads to incorrect UX assumptions about key portability.
  • Assuming the RP server ever handles or stores private key material violates the core security model.
  • Ignoring subdomain scoping rules causes credential isolation failures across staging/production environments.

Compliance Mapping: NIST SP 800-63B (AAL2/AAL3), ISO/IEC 27001 Annex A.9 (Access Control)

2. The Relying Party: Server-Side Responsibilities & State Management

The server-side component orchestrates cryptographic challenges and validates assertions without ever touching private keys. Architects designing cross-platform deployments should reference Understanding WebAuthn vs FIDO2 Architecture to align transport layers with RP expectations. The RP’s primary mandate is state management, cryptographic validation, and policy enforcement.

Core Server Workflows

  1. Generate 32-byte cryptographically secure challenge using crypto.randomBytes(32).
  2. Store pending challenge with a strict TTL (e.g., 300s) and bind it to the active session ID.
  3. Validate incoming assertion signature against the stored public key using COSE algorithm mapping.
  4. Verify credential counter monotonicity to detect cloned authenticators or replay attacks.

Node.js/Express Challenge Generation & Storage

import { Router, Request, Response } from 'express';
import crypto from 'crypto';
import { db } from './db'; // Abstracted DB client

const router = Router();

router.post('/webauthn/register/start', async (req: Request, res: Response) => {
 const challenge = crypto.randomBytes(32);
 const sessionId = req.sessionID;
 const expiresAt = new Date(Date.now() + 300_000); // 5 min TTL

 // Store pending challenge with strict expiration
 await db.query(
 'INSERT INTO pending_challenges (session_id, challenge, expires_at) VALUES ($1, $2, $3)',
 [sessionId, challenge.toString('base64url'), expiresAt]
 );

 res.json({
 challenge: challenge.toString('base64url'),
 rp: { id: process.env.RP_ID, name: 'Identity Platform' },
 user: { id: req.user.id, name: req.user.email, displayName: req.user.name },
 pubKeyCredParams: [{ alg: -7, type: 'public-key' }, { alg: -257, type: 'public-key' }]
 });
});

PostgreSQL Credential Schema

CREATE TABLE webauthn_credentials (
 id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
 credential_id BYTEA UNIQUE NOT NULL,
 user_id UUID NOT NULL REFERENCES users(id),
 public_key JSONB NOT NULL, -- Stores COSE key structure
 sign_count INTEGER NOT NULL DEFAULT 0,
 transports TEXT[] NOT NULL DEFAULT '{}',
 backup_eligible BOOLEAN DEFAULT FALSE,
 backup_state BOOLEAN DEFAULT FALSE,
 created_at TIMESTAMPTZ DEFAULT NOW(),
 revoked_at TIMESTAMPTZ
);

️ Implementation Pitfalls**

  • Hardcoding RP ID across environments breaks origin binding and causes NotAllowedError.
  • Failing to validate the signature algorithm against the stored COSE key enables downgrade attacks.
  • Ignoring counter drift thresholds leads to false-positive replay blocks on legitimate roaming devices.

Compliance Mapping: SOC 2 Type II (Logical Access Controls), GDPR Article 5 (Data Minimization & Storage Limitation)

3. The Authenticator: Platform vs Cross-Platform Execution

Authenticators isolate cryptographic operations within hardware-backed secure elements, ensuring private keys never leave the device boundary. Implementation choices directly impact credential portability and recovery workflows. Authenticators execute via the Client to Authenticator Protocol 2 (CTAP2) and are mediated by OS-level credential managers.

Execution & Validation Steps

  • Evaluate authenticator selection UI against navigator.credentials.isConditionalMediationAvailable() and device capabilities.
  • Enforce user verification (UV) requirements (required vs preferred) before key generation to meet phishing-resistance standards.
  • Handle resident key provisioning by setting residentKey: 'required' and parsing discoverable flags in the response.
  • Parse and relay attestation objects to the RP for chain verification when enterprise compliance mandates hardware provenance.

Client-Side Configuration & Error Handling

async function createPasskey(challenge: string, userId: string) {
 try {
 const credential = await navigator.credentials.create({
 publicKey: {
 challenge: Uint8Array.from(atob(challenge), c => c.charCodeAt(0)),
 rp: { id: window.location.hostname, name: 'Secure App' },
 user: { id: Uint8Array.from(atob(userId), c => c.charCodeAt(0)), name: '[email protected]', displayName: 'User' },
 pubKeyCredParams: [{ alg: -7, type: 'public-key' }],
 authenticatorSelection: {
 residentKey: 'required',
 userVerification: 'required',
 authenticatorAttachment: 'platform'
 },
 attestation: 'direct'
 }
 }) as PublicKeyCredential;

 return {
 id: credential.id,
 rawId: btoa(String.fromCharCode(...new Uint8Array(credential.rawId))),
 response: {
 clientDataJSON: btoa(String.fromCharCode(...new Uint8Array(credential.response.clientDataJSON))),
 attestationObject: btoa(String.fromCharCode(...new Uint8Array(credential.response.attestationObject)))
 }
 };
 } catch (err) {
 if (err instanceof DOMException) {
 if (err.name === 'NotAllowedError') throw new Error('UV_BLOCKED: User verification denied or timed out.');
 if (err.name === 'SecurityError') throw new Error('UV_NOT_ALLOWED: Origin mismatch or insecure context.');
 }
 throw err;
 }
}

️ Implementation Pitfalls**

  • Assuming UV is universally available on cross-platform roaming devices causes fallback failures.
  • Over-relying on resident keys without server-bound fallback storage complicates account recovery.
  • Ignoring transport filtering (transports: ['internal', 'hybrid']) degrades UX on unsupported hardware.

Compliance Mapping: FIPS 140-3 (Cryptographic Module Validation), eIDAS (QSCD requirements for qualified signatures)

4. Credential Lifecycle: Registration & Assertion Workflows

During credential provisioning, the choice of asymmetric cryptography dictates long-term security posture. Compare implementation trade-offs in Public Key vs Symmetric Credential Types when designing fallback authentication paths. The lifecycle spans registration (attestation), authentication (assertion), and ongoing credential management.

Registration & Assertion Flow Validation

  1. Parse COSE key formats from attestationObject and map to algorithm allowlists (e.g., ES256 -7, RS256 -257).
  2. Verify attestation chain against trusted root certificates if attestation: 'direct' or 'enterprise' is requested.
  3. Validate clientDataJSON hash matches the original challenge and verify type: "webauthn.create" or "webauthn.get".
  4. Enforce UP/UV flag checks in the authenticator data bitmask to confirm user presence and verification.

Server-Side Assertion Verification Middleware

import { verifyRegistrationResponse, verifyAuthenticationResponse } from '@simplewebauthn/server';
import { Request, Response, NextFunction } from 'express';

export const verifyAssertion = async (req: Request, res: Response, next: NextFunction) => {
 const { credentialId, clientDataJSON, authenticatorData, signature } = req.body;
 
 // Fetch stored credential from DB
 const storedCred = await db.query('SELECT * FROM webauthn_credentials WHERE credential_id = $1', [credentialId]);
 if (!storedCred) return res.status(404).json({ error: 'Credential not found' });

 const verification = await verifyAuthenticationResponse({
 response: {
 id: credentialId,
 rawId: credentialId,
 response: {
 clientDataJSON: clientDataJSON,
 authenticatorData: authenticatorData,
 signature: signature
 }
 },
 expectedChallenge: req.session.challenge,
 expectedOrigin: req.headers.origin!,
 expectedRPID: process.env.RP_ID!,
 credentialPublicKey: storedCred.public_key,
 credentialCurrentSignCount: storedCred.sign_count,
 requireUserVerification: true
 });

 if (verification.verified) {
 // Update monotonic counter
 await db.query('UPDATE webauthn_credentials SET sign_count = $1 WHERE credential_id = $2', [
 verification.authenticationInfo.newCounter,
 credentialId
 ]);
 next();
 } else {
 res.status(401).json({ error: 'Assertion verification failed', reason: verification.error });
 }
};

️ Implementation Pitfalls**

  • Accepting 'none' attestation in high-security enterprise contexts violates hardware provenance requirements.
  • Failing to parse COSE key formats correctly across elliptic curves causes signature validation crashes.
  • Not handling UP vs UV flags distinctly weakens phishing resistance guarantees.

Compliance Mapping: OWASP Authentication Cheat Sheet, PCI-DSS Requirement 8 (Multi-Factor Authentication)

5. Security Boundaries, Validation & Enterprise Compliance

Enterprise deployments require strict enforcement of cryptographic boundaries and audit trails. Implementing defense-in-depth controls is detailed in WebAuthn Security Boundaries for Enterprise Apps. The RP must act as the final gatekeeper, enforcing origin binding, algorithm agility, and counter monotonicity.

Validation & Enforcement Workflows

  • Implement strict origin validation middleware to reject mismatched clientDataJSON.origin before challenge generation.
  • Log assertion failures with anonymized device metadata (aaguid, transports) for threat intelligence and replay detection.
  • Enforce algorithm agility by maintaining an explicit allowlist and deprecating weak curves (e.g., restrict ES256K unless explicitly required by legacy hardware).
  • Audit backupEligible and backupState flags to synchronize with cloud sync policies and comply with data residency requirements.

Counter Drift Detection & Structured Logging

import winston from 'winston';

const logger = winston.createLogger({
 level: 'info',
 format: winston.format.json(),
 transports: [new winston.transports.File({ filename: 'webauthn-audit.log' })]
});

export function validateCounter(currentCount: number, storedCount: number, tolerance: number = 5): boolean {
 // Monotonicity check with configurable drift for roaming sync
 if (currentCount <= storedCount) {
 logger.warn({ event: 'counter_regression', current: currentCount, stored: storedCount });
 return false; // Potential clone or replay
 }
 if (currentCount - storedCount > tolerance) {
 logger.warn({ event: 'counter_drift_exceeded', current: currentCount, stored: storedCount });
 // Allow but flag for manual review
 }
 return true;
}

️ Implementation Pitfalls**

  • Disabling origin checks in staging environments and promoting to production exposes the RP to cross-site credential theft.
  • Ignoring algorithm agility requirements leads to vendor lock-in and breaks interoperability with newer authenticators.
  • Failing to log assertion failures obscures brute-force, credential stuffing, or replay attempts.

Compliance Mapping: NIST IR 8259-1 (IoT Device Cybersecurity), CISA Zero Trust Architecture (Identity Pillar), ISO/IEC 27017 (Cloud Security Controls)