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.
// Auto sign-in on page loadasync 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/loginasync 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 formasync 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 logoutasync 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 supportif (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.
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.
// Check permission statusasync 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 permissionasync 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 permissionasync 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 permissionasync 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 permissionsasync 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 UIasync 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").
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.
// Hash string with SHA-256async 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;}// Usageconst hash = await hashString("Hello, World!");console.log("SHA-256 hash:", hash);
Example: Encrypt/decrypt with AES-GCM
// Generate AES-GCM keyasync function generateAESKey() { const key = await crypto.subtle.generateKey( { "name": "AES-GCM", "length": 256 }, true, // extractable ["encrypt", "decrypt"] ); return key;}// Encrypt dataasync 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 dataasync 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);}// Usageconst 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