Authentication and Security APIs

1. Web Authentication (WebAuthn) API

Method Description Browser Support
navigator.credentials.create(options) Creates new credential (registration). Options include publicKey for WebAuthn. All Modern Browsers
navigator.credentials.get(options) Gets credential for authentication. Options include publicKey for WebAuthn. All Modern Browsers
PublicKeyCredential Property Type Description
id string Base64url-encoded credential ID.
rawId ArrayBuffer Raw credential ID bytes.
type string Always "public-key" for WebAuthn.
response AuthenticatorResponse Authenticator response (attestation or assertion).

Example: WebAuthn registration

// Registration (create new credential)
async function registerWebAuthn(username) {
  try {
    // Get challenge from server
    const challengeResponse = await fetch("/auth/register/challenge", {
      "method": "POST",
      "headers": { "Content-Type": "application/json" },
      "body": JSON.stringify({ "username": username })
    });
    
    const { "challenge": challenge, "userId": userId } = await challengeResponse.json();
    
    // Create credential
    const credential = await navigator.credentials.create({
      "publicKey": {
        "challenge": base64ToArrayBuffer(challenge),
        "rp": {
          "name": "My App",
          "id": "example.com" // Domain without protocol/port
        },
        "user": {
          "id": base64ToArrayBuffer(userId),
          "name": username,
          "displayName": username
        },
        "pubKeyCredParams": [
          { "type": "public-key", "alg": -7 }, // ES256
          { "type": "public-key", "alg": -257 } // RS256
        ],
        "timeout": 60000, // 60 seconds
        "attestation": "none", // "none", "indirect", or "direct"
        "authenticatorSelection": {
          "authenticatorAttachment": "platform", // "platform" (built-in) or "cross-platform" (USB key)
          "requireResidentKey": false,
          "userVerification": "preferred" // "required", "preferred", or "discouraged"
        }
      }
    });
    
    // Send credential to server for verification
    const registrationResponse = await fetch("/auth/register/verify", {
      "method": "POST",
      "headers": { "Content-Type": "application/json" },
      "body": JSON.stringify({
        "id": credential.id,
        "rawId": arrayBufferToBase64(credential.rawId),
        "type": credential.type,
        "response": {
          "clientDataJSON": arrayBufferToBase64(credential.response.clientDataJSON),
          "attestationObject": arrayBufferToBase64(credential.response.attestationObject)
        }
      })
    });
    
    const result = await registrationResponse.json();
    console.log("Registration successful:", result);
    
    return result;
  } catch (error) {
    console.error("WebAuthn registration failed:", error);
    throw error;
  }
}

// Helper functions
function base64ToArrayBuffer(base64) {
  const binary = atob(base64.replace(/-/g, "+").replace(/_/g, "/"));
  const bytes = new Uint8Array(binary.length);
  for (let i = 0; i < binary.length; i++) {
    bytes[i] = binary.charCodeAt(i);
  }
  return bytes.buffer;
}

function arrayBufferToBase64(buffer) {
  const bytes = new Uint8Array(buffer);
  let binary = "";
  for (let i = 0; i < bytes.length; i++) {
    binary += String.fromCharCode(bytes[i]);
  }
  return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
}

Example: WebAuthn authentication

// Authentication (get existing credential)
async function authenticateWebAuthn(username) {
  try {
    // Get challenge from server
    const challengeResponse = await fetch("/auth/login/challenge", {
      "method": "POST",
      "headers": { "Content-Type": "application/json" },
      "body": JSON.stringify({ "username": username })
    });
    
    const { "challenge": challenge, "allowCredentials": allowCredentials } = await challengeResponse.json();
    
    // Get credential
    const credential = await navigator.credentials.get({
      "publicKey": {
        "challenge": base64ToArrayBuffer(challenge),
        "timeout": 60000,
        "rpId": "example.com",
        "allowCredentials": allowCredentials.map(cred => ({
          "type": "public-key",
          "id": base64ToArrayBuffer(cred.id)
        })),
        "userVerification": "preferred"
      }
    });
    
    // Send assertion to server for verification
    const authResponse = await fetch("/auth/login/verify", {
      "method": "POST",
      "headers": { "Content-Type": "application/json" },
      "body": JSON.stringify({
        "id": credential.id,
        "rawId": arrayBufferToBase64(credential.rawId),
        "type": credential.type,
        "response": {
          "clientDataJSON": arrayBufferToBase64(credential.response.clientDataJSON),
          "authenticatorData": arrayBufferToBase64(credential.response.authenticatorData),
          "signature": arrayBufferToBase64(credential.response.signature),
          "userHandle": credential.response.userHandle ? 
                        arrayBufferToBase64(credential.response.userHandle) : null
        }
      })
    });
    
    const result = await authResponse.json();
    console.log("Authentication successful:", result);
    
    return result;
  } catch (error) {
    console.error("WebAuthn authentication failed:", error);
    throw error;
  }
}

// Check browser support
if (window.PublicKeyCredential) {
  console.log("WebAuthn supported");
  
  // Check platform authenticator availability
  PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()
    .then(available => {
      console.log("Platform authenticator available:", available);
    });
} else {
  console.log("WebAuthn not supported");
}
Note: WebAuthn enables passwordless authentication using biometrics or security keys. More secure than passwords - phishing resistant. Requires HTTPS. Server must validate attestation/assertion. Use platform authenticators (Face ID, Touch ID, Windows Hello) for best UX.
Warning: WebAuthn requires server-side implementation for challenge generation and verification. Always use proper challenge validation to prevent replay attacks. Test on multiple platforms - behavior varies. Fallback to password auth for browsers without support.

2. Credential Management API for Password Storage

Method Description Browser Support
navigator.credentials.create(options) Creates credential. Options: password for PasswordCredential. Chrome, Edge
navigator.credentials.get(options) Gets stored credential. Options: password, mediation. Chrome, Edge
navigator.credentials.store(credential) Stores credential in browser's password manager. Chrome, Edge
navigator.credentials.preventSilentAccess() Prevents automatic sign-in after sign-out. Chrome, Edge
PasswordCredential Property Type Description
id string Username/email.
password string Password.
name string Display name.
iconURL string Icon URL.

Example: Password credential management

// Auto sign-in on page load
async function autoSignIn() {
  try {
    // Get stored credential
    const credential = await navigator.credentials.get({
      "password": true,
      "mediation": "optional" // "optional", "silent", or "required"
    });
    
    if (credential) {
      console.log("Credential found:", credential.id);
      
      // Use credential to sign in
      const response = await fetch("/auth/login", {
        "method": "POST",
        "headers": { "Content-Type": "application/json" },
        "body": JSON.stringify({
          "username": credential.id,
          "password": credential.password
        })
      });
      
      if (response.ok) {
        console.log("Auto sign-in successful");
        return true;
      }
    } else {
      console.log("No stored credentials");
    }
  } catch (error) {
    console.error("Auto sign-in failed:", error);
  }
  
  return false;
}

// Store credential after successful registration/login
async function storeCredential(username, password, name) {
  try {
    // Create password credential
    const credential = new PasswordCredential({
      "id": username,
      "password": password,
      "name": name,
      "iconURL": "/images/avatar.png"
    });
    
    // Store credential
    await navigator.credentials.store(credential);
    console.log("Credential stored");
    
    return true;
  } catch (error) {
    console.error("Failed to store credential:", error);
    return false;
  }
}

// Alternative: create from form
async function storeCredentialFromForm() {
  const form = document.querySelector("form");
  
  try {
    const credential = await navigator.credentials.create({
      "password": form
    });
    
    await navigator.credentials.store(credential);
    console.log("Credential from form stored");
  } catch (error) {
    console.error("Failed to store credential from form:", error);
  }
}

// Prevent auto sign-in after logout
async function signOut() {
  // Prevent silent access on next visit
  await navigator.credentials.preventSilentAccess();
  
  console.log("Signed out - auto sign-in disabled");
  
  // Clear session
  await fetch("/auth/logout", { "method": "POST" });
}

// Check support
if (window.PasswordCredential) {
  console.log("Credential Management API supported");
  
  // Try auto sign-in
  autoSignIn().then(success => {
    if (!success) {
      // Show login form
      document.querySelector(".login-form").style.display = "block";
    }
  });
} else {
  console.log("Credential Management API not supported");
}
Note: Credential Management API has limited browser support (Chrome, Edge). Integrates with browser's password manager. Use mediation: "silent" for automatic sign-in, "required" to always show account picker. Always call preventSilentAccess() on sign-out.
Warning: API doesn't store passwords itself - relies on browser's password manager. Not available in Safari/Firefox. Always provide fallback manual login. Don't rely on this for security - still validate on server.

3. Payment Request API for Web Payments

Method Description Browser Support
new PaymentRequest(methods, details, options) Creates payment request. Methods: payment methods, details: items/total, options: shipping. Chrome, Edge, Safari
request.show() Shows payment UI. Returns Promise<PaymentResponse>. Requires user gesture. Chrome, Edge, Safari
request.canMakePayment() Checks if user can make payment. Returns Promise<boolean>. Chrome, Edge, Safari
request.abort() Aborts payment request. Chrome, Edge, Safari
PaymentResponse Method Description
response.complete(result) Completes payment. Result: "success", "fail", or "unknown".
response.retry(errors) Retries payment with error messages.

Example: Payment Request API

// Process payment
async function processPayment() {
  try {
    // Check support
    if (!window.PaymentRequest) {
      console.log("Payment Request API not supported");
      return;
    }
    
    // Define payment methods
    const paymentMethods = [
      {
        "supportedMethods": "basic-card",
        "data": {
          "supportedNetworks": ["visa", "mastercard", "amex"],
          "supportedTypes": ["debit", "credit"]
        }
      }
    ];
    
    // Define payment details
    const paymentDetails = {
      "total": {
        "label": "Total",
        "amount": { "currency": "USD", "value": "99.99" }
      },
      "displayItems": [
        {
          "label": "Product",
          "amount": { "currency": "USD", "value": "89.99" }
        },
        {
          "label": "Shipping",
          "amount": { "currency": "USD", "value": "10.00" }
        }
      ]
    };
    
    // Optional: shipping options
    const options = {
      "requestShipping": true,
      "requestPayerName": true,
      "requestPayerEmail": true,
      "requestPayerPhone": true,
      "shippingOptions": [
        {
          "id": "standard",
          "label": "Standard Shipping",
          "amount": { "currency": "USD", "value": "10.00" },
          "selected": true
        },
        {
          "id": "express",
          "label": "Express Shipping",
          "amount": { "currency": "USD", "value": "20.00" }
        }
      ]
    };
    
    // Create payment request
    const request = new PaymentRequest(paymentMethods, paymentDetails, options);
    
    // Check if user can make payment
    const canMakePayment = await request.canMakePayment();
    if (!canMakePayment) {
      console.log("User cannot make payment");
      return;
    }
    
    // Listen for shipping address change
    request.addEventListener("shippingaddresschange", async (e) => {
      // Update shipping options based on address
      e.updateWith(new Promise((resolve) => {
        // Calculate shipping based on address
        const shippingAddress = request.shippingAddress;
        console.log("Shipping to:", shippingAddress.country);
        
        // Update details
        resolve({
          "total": {
            "label": "Total",
            "amount": { "currency": "USD", "value": "99.99" }
          },
          "shippingOptions": options.shippingOptions
        });
      }));
    });
    
    // Listen for shipping option change
    request.addEventListener("shippingoptionchange", async (e) => {
      const shippingOption = request.shippingOption;
      console.log("Shipping option:", shippingOption);
      
      // Update total
      const shippingCost = shippingOption === "express" ? "20.00" : "10.00";
      const total = (89.99 + parseFloat(shippingCost)).toFixed(2);
      
      e.updateWith({
        "total": {
          "label": "Total",
          "amount": { "currency": "USD", "value": total }
        }
      });
    });
    
    // Show payment UI
    const response = await request.show();
    
    // Get payment details
    console.log("Card number:", response.details.cardNumber);
    console.log("Payer name:", response.payerName);
    console.log("Payer email:", response.payerEmail);
    console.log("Shipping address:", response.shippingAddress);
    
    // Process payment on server
    const serverResponse = await fetch("/api/process-payment", {
      "method": "POST",
      "headers": { "Content-Type": "application/json" },
      "body": JSON.stringify({
        "paymentMethod": response.methodName,
        "details": response.details,
        "shippingAddress": response.shippingAddress,
        "total": paymentDetails.total.amount.value
      })
    });
    
    if (serverResponse.ok) {
      // Complete payment successfully
      await response.complete("success");
      console.log("Payment successful");
    } else {
      // Payment failed
      await response.complete("fail");
      console.log("Payment failed");
    }
    
  } catch (error) {
    console.error("Payment error:", error);
    
    if (error.name === "AbortError") {
      console.log("Payment cancelled by user");
    }
  }
}

// Trigger on button click (requires user gesture)
document.getElementById("pay-button").addEventListener("click", processPayment);
Note: Payment Request API provides native payment UI across browsers. Supports credit cards, digital wallets (Apple Pay, Google Pay). Must call show() from user gesture. Always call complete() to close payment UI.
Warning: Payment Request API only handles UI - doesn't process payments. Must implement server-side payment processing. Never trust client-side data - validate on server. Test on mobile - UX varies by platform.

4. Permissions API for Feature Access Control

Method Description Browser Support
navigator.permissions.query(descriptor) Queries permission state. Descriptor: { name: "camera" }. Returns Promise<PermissionStatus>. All Modern Browsers
Permission Name Description Support
camera Camera access (getUserMedia). Good
microphone Microphone access (getUserMedia). Good
geolocation Location access. Good
notifications Notification permission. Good
push Push notification permission. Modern
persistent-storage Persistent storage quota. Limited
PermissionStatus Property Type Description
state string "granted", "denied", or "prompt".
onchange EventHandler Fires when permission state changes.

Example: Check and request permissions

// Check permission status
async function checkPermission(name) {
  try {
    const status = await navigator.permissions.query({ "name": name });
    
    console.log(`${name} permission:`, status.state);
    
    // Listen for changes
    status.addEventListener("change", () => {
      console.log(`${name} permission changed to:`, status.state);
    });
    
    return status.state;
  } catch (error) {
    console.error(`Failed to query ${name} permission:`, error);
    return null;
  }
}

// Camera permission
async function requestCameraAccess() {
  const status = await checkPermission("camera");
  
  if (status === "granted") {
    console.log("Camera already granted");
    return true;
  } else if (status === "denied") {
    console.log("Camera denied - show instructions to enable");
    return false;
  } else if (status === "prompt") {
    console.log("Camera will prompt user");
    
    // Request access (triggers permission prompt)
    try {
      const stream = await navigator.mediaDevices.getUserMedia({ "video": true });
      stream.getTracks().forEach(track => track.stop());
      return true;
    } catch (error) {
      console.error("Camera access denied:", error);
      return false;
    }
  }
}

// Geolocation permission
async function requestLocationAccess() {
  const status = await navigator.permissions.query({ "name": "geolocation" });
  
  console.log("Geolocation permission:", status.state);
  
  if (status.state === "granted") {
    // Already granted - get location
    navigator.geolocation.getCurrentPosition(
      (position) => console.log("Location:", position.coords),
      (error) => console.error("Location error:", error)
    );
  } else {
    // Will prompt or is denied
    navigator.geolocation.getCurrentPosition(
      (position) => console.log("Location granted:", position.coords),
      (error) => console.error("Location denied:", error)
    );
  }
}

// Notification permission
async function requestNotificationAccess() {
  const status = await navigator.permissions.query({ "name": "notifications" });
  
  console.log("Notification permission:", status.state);
  
  if (status.state === "prompt") {
    // Request permission
    const result = await Notification.requestPermission();
    console.log("Notification permission result:", result);
  }
}

// Check multiple permissions
async function checkMultiplePermissions() {
  const permissions = ["camera", "microphone", "geolocation", "notifications"];
  
  const results = await Promise.all(
    permissions.map(async (name) => {
      try {
        const status = await navigator.permissions.query({ "name": name });
        return { "name": name, "state": status.state };
      } catch (error) {
        return { "name": name, "state": "unsupported" };
      }
    })
  );
  
  console.log("Permission states:");
  results.forEach(({ "name": name, "state": state }) => {
    console.log(`  ${name}: ${state}`);
  });
  
  return results;
}

// Permission-aware UI
async function updateUIBasedOnPermissions() {
  const cameraStatus = await checkPermission("camera");
  
  const cameraButton = document.getElementById("camera-button");
  
  if (cameraStatus === "granted") {
    cameraButton.textContent = "Open Camera";
    cameraButton.disabled = false;
  } else if (cameraStatus === "denied") {
    cameraButton.textContent = "Camera Denied";
    cameraButton.disabled = true;
    // Show instructions to enable in settings
  } else {
    cameraButton.textContent = "Allow Camera";
    cameraButton.disabled = false;
  }
}
Note: Permissions API only queries state - doesn't request permission. To request, use the actual API (getUserMedia, Notification.requestPermission, etc.). Use onchange to update UI when user changes permission in browser settings.
Warning: Not all permissions queryable on all browsers. Some permissions always return "prompt" state. Don't rely solely on Permissions API - handle permission errors when using actual APIs. Browser support varies by permission type.

5. Content Security Policy (CSP) Reporting API

Event Description Browser Support
securitypolicyviolation Fires when CSP violation occurs. Event has violation details. All Modern Browsers
SecurityPolicyViolationEvent Property Type Description
violatedDirective string CSP directive that was violated (e.g., "script-src").
effectiveDirective string Effective directive that caused violation.
blockedURI string URI that was blocked.
sourceFile string File where violation occurred.
lineNumber number Line number of violation.
columnNumber number Column number of violation.
disposition string "enforce" or "report" (report-only mode).

Example: CSP violation monitoring

// Listen for CSP violations
document.addEventListener("securitypolicyviolation", (e) => {
  console.warn("CSP Violation detected:");
  console.log("Violated directive:", e.violatedDirective);
  console.log("Effective directive:", e.effectiveDirective);
  console.log("Blocked URI:", e.blockedURI);
  console.log("Source file:", e.sourceFile);
  console.log("Line:", e.lineNumber, "Column:", e.columnNumber);
  console.log("Disposition:", e.disposition);
  console.log("Original policy:", e.originalPolicy);
  
  // Send violation to analytics
  sendCSPViolation({
    "directive": e.violatedDirective,
    "blockedURI": e.blockedURI,
    "sourceFile": e.sourceFile,
    "lineNumber": e.lineNumber,
    "disposition": e.disposition,
    "timestamp": Date.now(),
    "userAgent": navigator.userAgent
  });
});

// Send CSP violations to server
function sendCSPViolation(violation) {
  // Use sendBeacon for reliability
  if (navigator.sendBeacon) {
    navigator.sendBeacon("/api/csp-violations", JSON.stringify(violation));
  } else {
    fetch("/api/csp-violations", {
      "method": "POST",
      "headers": { "Content-Type": "application/json" },
      "body": JSON.stringify(violation)
    }).catch(error => console.error("Failed to send CSP violation:", error));
  }
}

// CSP violation aggregator
class CSPMonitor {
  constructor() {
    this.violations = [];
    this.listeners = [];
    
    document.addEventListener("securitypolicyviolation", (e) => {
      this.handleViolation(e);
    });
  }
  
  handleViolation(event) {
    const violation = {
      "directive": event.violatedDirective,
      "effectiveDirective": event.effectiveDirective,
      "blockedURI": event.blockedURI,
      "sourceFile": event.sourceFile,
      "lineNumber": event.lineNumber,
      "columnNumber": event.columnNumber,
      "disposition": event.disposition,
      "timestamp": Date.now()
    };
    
    this.violations.push(violation);
    
    // Notify listeners
    this.listeners.forEach(callback => callback(violation));
    
    // Auto-report after 10 violations or 5 seconds
    if (this.violations.length >= 10) {
      this.report();
    } else if (this.violations.length === 1) {
      setTimeout(() => this.report(), 5000);
    }
  }
  
  report() {
    if (this.violations.length === 0) return;
    
    const violations = this.violations.slice();
    this.violations = [];
    
    console.log(`Reporting ${violations.length} CSP violations`);
    
    fetch("/api/csp-violations/batch", {
      "method": "POST",
      "headers": { "Content-Type": "application/json" },
      "body": JSON.stringify({ "violations": violations })
    }).catch(error => console.error("Failed to report CSP violations:", error));
  }
  
  onViolation(callback) {
    this.listeners.push(callback);
  }
  
  getViolations() {
    return this.violations.slice();
  }
}

// Initialize monitor
const cspMonitor = new CSPMonitor();

cspMonitor.onViolation((violation) => {
  console.warn("CSP violation:", violation);
  
  // Show warning in dev mode
  if (process.env.NODE_ENV === "development") {
    console.warn("Fix CSP violation:", violation.blockedURI);
  }
});
Note: CSP violations fire securitypolicyviolation event. Use to monitor and fix CSP issues. Set CSP in report-only mode during development with Content-Security-Policy-Report-Only header. Aggregate violations before sending to reduce requests.

6. SubtleCrypto API for Cryptographic Operations

Method Description Browser Support
crypto.subtle.encrypt(algorithm, key, data) Encrypts data. Returns Promise<ArrayBuffer>. All Modern Browsers
crypto.subtle.decrypt(algorithm, key, data) Decrypts data. Returns Promise<ArrayBuffer>. All Modern Browsers
crypto.subtle.digest(algorithm, data) Hashes data. Algorithm: "SHA-1", "SHA-256", "SHA-384", "SHA-512". All Modern Browsers
crypto.subtle.generateKey(algorithm, extractable, keyUsages) Generates key. Returns Promise<CryptoKey>. All Modern Browsers
crypto.subtle.importKey(format, keyData, algorithm, extractable, keyUsages) Imports key. Format: "raw", "pkcs8", "spki", "jwk". All Modern Browsers
crypto.subtle.exportKey(format, key) Exports key. Returns Promise<ArrayBuffer | JsonWebKey>. All Modern Browsers
crypto.subtle.sign(algorithm, key, data) Signs data. Returns Promise<ArrayBuffer>. All Modern Browsers
crypto.subtle.verify(algorithm, key, signature, data) Verifies signature. Returns Promise<boolean>. All Modern Browsers

Example: Hash data with SHA-256

// Hash string with SHA-256
async function hashString(message) {
  // Encode string to bytes
  const encoder = new TextEncoder();
  const data = encoder.encode(message);
  
  // Hash data
  const hashBuffer = await crypto.subtle.digest("SHA-256", data);
  
  // Convert to hex string
  const hashArray = Array.from(new Uint8Array(hashBuffer));
  const hashHex = hashArray.map(b => b.toString(16).padStart(2, "0")).join("");
  
  return hashHex;
}

// Usage
const hash = await hashString("Hello, World!");
console.log("SHA-256 hash:", hash);

Example: Encrypt/decrypt with AES-GCM

// Generate AES-GCM key
async function generateAESKey() {
  const key = await crypto.subtle.generateKey(
    {
      "name": "AES-GCM",
      "length": 256
    },
    true, // extractable
    ["encrypt", "decrypt"]
  );
  
  return key;
}

// Encrypt data
async function encryptData(key, data) {
  const encoder = new TextEncoder();
  const encoded = encoder.encode(data);
  
  // Generate random IV (initialization vector)
  const iv = crypto.getRandomValues(new Uint8Array(12));
  
  // Encrypt
  const encrypted = await crypto.subtle.encrypt(
    {
      "name": "AES-GCM",
      "iv": iv
    },
    key,
    encoded
  );
  
  // Return IV + encrypted data
  return {
    "iv": Array.from(iv),
    "data": Array.from(new Uint8Array(encrypted))
  };
}

// Decrypt data
async function decryptData(key, encryptedData) {
  const decrypted = await crypto.subtle.decrypt(
    {
      "name": "AES-GCM",
      "iv": new Uint8Array(encryptedData.iv)
    },
    key,
    new Uint8Array(encryptedData.data)
  );
  
  const decoder = new TextDecoder();
  return decoder.decode(decrypted);
}

// Usage
const key = await generateAESKey();
const encrypted = await encryptData(key, "Secret message");
const decrypted = await decryptData(key, encrypted);
console.log("Decrypted:", decrypted);
Note: SubtleCrypto API is for cryptographic operations - hashing, encryption, signing. Only available in secure contexts (HTTPS). Use AES-GCM for encryption, SHA-256 for hashing, RSA/ECDSA for signing. All methods return Promises.
Warning: Crypto is complex - use established libraries when possible. Never implement your own crypto algorithms. Always use random IVs for encryption. Don't use for password storage - use server-side bcrypt/argon2. SubtleCrypto requires HTTPS.

Authentication and Security Best Practices

  • Use WebAuthn for passwordless authentication - more secure than passwords
  • Implement proper challenge validation for WebAuthn on server
  • Credential Management API has limited support - provide fallback
  • Payment Request API only handles UI - implement server-side processing
  • Use Permissions API to check state before requesting - better UX
  • Monitor CSP violations with securitypolicyviolation event
  • Use SubtleCrypto for client-side encryption - requires HTTPS
  • Never implement custom crypto - use established algorithms
  • Always validate and sanitize data on server - never trust client
  • Use HTTPS for all security-sensitive operations
  • Test security features across browsers - support varies
  • Provide clear error messages when permissions denied