or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

authentication.mdbrowser-support.mddata-encoding.mderror-handling.mdindex.mdregistration.mdserver.md
tile.json

error-handling.mddocs/

Error Handling and Services

Comprehensive error handling with detailed error codes and abort service for managing concurrent WebAuthn operations. These utilities provide detailed error information and control over WebAuthn ceremony lifecycle.

Capabilities

WebAuthn Error Class

Custom Error class that provides nuanced error details for WebAuthn operations, helping developers understand and handle specific failure scenarios.

/**
 * A custom Error used to return a more nuanced error detailing _why_ one of the eight documented
 * errors in the spec was raised after calling `navigator.credentials.create()` or
 * `navigator.credentials.get()`:
 * 
 * - `AbortError`
 * - `ConstraintError`
 * - `InvalidStateError`
 * - `NotAllowedError`
 * - `NotSupportedError`
 * - `SecurityError`
 * - `TypeError`
 * - `UnknownError`
 */
class WebAuthnError extends Error {
  code: WebAuthnErrorCode;
  
  constructor(options: {
    message: string;
    code: WebAuthnErrorCode;
    cause: Error;
    name?: string;
  });
}

Usage Examples:

import { startRegistration, WebAuthnError } from "@simplewebauthn/browser";

try {
  const response = await startRegistration({ optionsJSON });
} catch (error) {
  if (error instanceof WebAuthnError) {
    console.log(`WebAuthn Error: ${error.code}`);
    console.log(`Message: ${error.message}`);
    console.log(`Original error:`, error.cause);
    
    // Handle specific error types
    switch (error.code) {
      case 'ERROR_CEREMONY_ABORTED':
        console.log("User cancelled the operation");
        break;
      case 'ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED':
        console.log("This authenticator is already registered");
        break;
      case 'ERROR_INVALID_DOMAIN':
        console.log("WebAuthn not allowed on this domain");
        break;
      default:
        console.log("Unexpected WebAuthn error occurred");
    }
  } else {
    console.error("Non-WebAuthn error:", error);
  }
}

WebAuthn Error Codes

Detailed error codes that provide specific information about WebAuthn failures.

type WebAuthnErrorCode =
  | 'ERROR_CEREMONY_ABORTED'
  | 'ERROR_INVALID_DOMAIN'
  | 'ERROR_INVALID_RP_ID'
  | 'ERROR_INVALID_USER_ID_LENGTH'
  | 'ERROR_MALFORMED_PUBKEYCREDPARAMS'
  | 'ERROR_AUTHENTICATOR_GENERAL_ERROR'
  | 'ERROR_AUTHENTICATOR_MISSING_DISCOVERABLE_CREDENTIAL_SUPPORT'
  | 'ERROR_AUTHENTICATOR_MISSING_USER_VERIFICATION_SUPPORT'
  | 'ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED'
  | 'ERROR_AUTHENTICATOR_NO_SUPPORTED_PUBKEYCREDPARAMS_ALG'
  | 'ERROR_AUTO_REGISTER_USER_VERIFICATION_FAILURE'
  | 'ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY';

Error Code Meanings:

  • ERROR_CEREMONY_ABORTED: User cancelled the WebAuthn operation
  • ERROR_INVALID_DOMAIN: WebAuthn not allowed on the current domain
  • ERROR_INVALID_RP_ID: Relying Party ID is invalid or mismatched
  • ERROR_INVALID_USER_ID_LENGTH: User ID exceeds maximum allowed length
  • ERROR_MALFORMED_PUBKEYCREDPARAMS: Invalid public key credential parameters
  • ERROR_AUTHENTICATOR_GENERAL_ERROR: General authenticator hardware/software error
  • ERROR_AUTHENTICATOR_MISSING_DISCOVERABLE_CREDENTIAL_SUPPORT: Authenticator doesn't support resident/discoverable credentials
  • ERROR_AUTHENTICATOR_MISSING_USER_VERIFICATION_SUPPORT: Authenticator can't perform required user verification
  • ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED: Attempting to register an already-registered authenticator
  • ERROR_AUTHENTICATOR_NO_SUPPORTED_PUBKEYCREDPARAMS_ALG: No supported cryptographic algorithms
  • ERROR_AUTO_REGISTER_USER_VERIFICATION_FAILURE: Auto-registration failed user verification
  • ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY: Error details are in the cause property

WebAuthn Abort Service

Service singleton to ensure only a single WebAuthn ceremony is active at a time, preventing conflicts between concurrent operations.

/**
 * A service singleton to help ensure that only a single WebAuthn ceremony is active at a time.
 * 
 * Users of **@simplewebauthn/browser** shouldn't typically need to use this, but it can help e.g.
 * developers building projects that use client-side routing to better control the behavior of
 * their UX in response to router navigation events.
 */
interface WebAuthnAbortService {
  /**
   * Prepare an abort signal that will help support multiple auth attempts without needing to
   * reload the page. This is automatically called whenever `startRegistration()` and
   * `startAuthentication()` are called.
   */
  createNewAbortSignal(): AbortSignal;
  
  /**
   * Manually cancel any active WebAuthn registration or authentication attempt.
   */
  cancelCeremony(): void;
}

// Exported as a singleton instance
const WebAuthnAbortService: WebAuthnAbortService;

Usage Examples:

import { WebAuthnAbortService, startRegistration } from "@simplewebauthn/browser";

// Manual ceremony cancellation (advanced usage)
function setupCancelButton() {
  const cancelButton = document.getElementById('cancel-webauthn');
  cancelButton.addEventListener('click', () => {
    WebAuthnAbortService.cancelCeremony();
    console.log("WebAuthn ceremony cancelled");
  });
}

// Router navigation cleanup (SPA usage)
function onRouteChange() {
  // Cancel any ongoing WebAuthn operations when navigating
  WebAuthnAbortService.cancelCeremony();
}

// The service is used automatically by startRegistration/startAuthentication
try {
  // This automatically calls WebAuthnAbortService.createNewAbortSignal()
  const response = await startRegistration({ optionsJSON });
} catch (error) {
  if (error.name === 'AbortError') {
    console.log("WebAuthn operation was aborted");
  }
}

// Advanced: Manual abort signal creation
const abortSignal = WebAuthnAbortService.createNewAbortSignal();
console.log("New abort signal created for WebAuthn operation");

Error Handling Patterns

Basic Error Handling

import { startRegistration, startAuthentication, WebAuthnError } from "@simplewebauthn/browser";

async function handleWebAuthnRegistration(options) {
  try {
    const response = await startRegistration({ optionsJSON: options });
    return { success: true, data: response };
  } catch (error) {
    if (error instanceof WebAuthnError) {
      return { 
        success: false, 
        error: error.code,
        message: error.message 
      };
    }
    return { 
      success: false, 
      error: 'UNKNOWN_ERROR',
      message: error.message 
    };
  }
}

User-Friendly Error Messages

function getErrorMessage(error: WebAuthnError): string {
  switch (error.code) {
    case 'ERROR_CEREMONY_ABORTED':
      return "Authentication was cancelled. Please try again.";
    case 'ERROR_INVALID_DOMAIN':
      return "Authentication is not allowed on this website.";
    case 'ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED':
      return "This security key has already been registered.";
    case 'ERROR_AUTHENTICATOR_MISSING_USER_VERIFICATION_SUPPORT':
      return "Your security key doesn't support the required verification method.";
    case 'ERROR_AUTHENTICATOR_NO_SUPPORTED_PUBKEYCREDPARAMS_ALG':
      return "Your security key doesn't support the required encryption methods.";
    default:
      return "An unexpected error occurred during authentication.";
  }
}

// Usage
try {
  await startRegistration({ optionsJSON });
} catch (error) {
  if (error instanceof WebAuthnError) {
    showUserError(getErrorMessage(error));
  }
}

Retry Logic with Error Handling

async function retryableWebAuthnOperation(operationFn, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await operationFn();
    } catch (error) {
      if (error instanceof WebAuthnError) {
        // Don't retry certain errors
        const nonRetryableErrors = [
          'ERROR_INVALID_DOMAIN',
          'ERROR_INVALID_RP_ID',
          'ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED',
        ];
        
        if (nonRetryableErrors.includes(error.code)) {
          throw error; // Don't retry these
        }
        
        if (attempt === maxRetries) {
          throw error; // Final attempt failed
        }
        
        console.log(`Attempt ${attempt} failed: ${error.code}, retrying...`);
        await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
      } else {
        throw error; // Non-WebAuthn errors aren't retryable
      }
    }
  }
}

// Usage
try {
  const response = await retryableWebAuthnOperation(() =>
    startRegistration({ optionsJSON })
  );
} catch (error) {
  console.error("All retry attempts failed:", error);
}

Service Integration

The abort service integrates seamlessly with the main WebAuthn functions:

import { startRegistration, startAuthentication, WebAuthnAbortService } from "@simplewebauthn/browser";

// Both functions automatically use the abort service
const registrationPromise = startRegistration({ optionsJSON: regOptions });
const authenticationPromise = startAuthentication({ optionsJSON: authOptions });

// If you start a new operation, the previous one is automatically cancelled
// This prevents conflicts and ensures only one ceremony runs at a time

// Manual cancellation is available for advanced use cases
document.getElementById('cancel').addEventListener('click', () => {
  WebAuthnAbortService.cancelCeremony();
});

This service is particularly useful in single-page applications where users might navigate between pages or trigger multiple authentication attempts.