import React, { useState, useEffect } from 'react'; import { CryptoAuthService } from '../../lib/auth/cryptoAuthService'; import { useAuth } from '../../context/AuthContext'; import { useNotifications } from '../../context/NotificationContext'; import { checkBrowserSupport, isSecureContext } from '../../lib/utils/browser'; interface CryptIDProps { onSuccess?: () => void; onCancel?: () => void; } /** * CryptID - WebCryptoAPI-based authentication component */ const CryptID: React.FC = ({ onSuccess, onCancel }) => { const [username, setUsername] = useState(''); const [isRegistering, setIsRegistering] = useState(false); const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(false); const [existingUsers, setExistingUsers] = useState([]); const [suggestedUsername, setSuggestedUsername] = useState(''); const [browserSupport, setBrowserSupport] = useState<{ supported: boolean; secure: boolean; webcrypto: boolean; }>({ supported: false, secure: false, webcrypto: false }); const { setSession } = useAuth(); const { addNotification } = useNotifications(); // Check browser support and existing users on mount useEffect(() => { const checkSupport = () => { const supported = checkBrowserSupport(); const secure = isSecureContext(); const webcrypto = typeof window !== 'undefined' && typeof window.crypto !== 'undefined' && typeof window.crypto.subtle !== 'undefined'; setBrowserSupport({ supported, secure, webcrypto }); if (!supported) { setError('Your browser does not support the required features for cryptographic authentication.'); addNotification('Browser not supported for cryptographic authentication', 'warning'); } else if (!secure) { setError('Cryptographic authentication requires a secure context (HTTPS).'); addNotification('Secure context required for cryptographic authentication', 'warning'); } else if (!webcrypto) { setError('WebCryptoAPI is not available in your browser.'); addNotification('WebCryptoAPI not available', 'warning'); } }; const checkExistingUsers = () => { try { // Get registered users from localStorage const users = JSON.parse(localStorage.getItem('registeredUsers') || '[]'); // Filter users to only include those with valid authentication keys const validUsers = users.filter((user: string) => { // Check if public key exists const publicKey = localStorage.getItem(`${user}_publicKey`); if (!publicKey) return false; // Check if authentication data exists const authData = localStorage.getItem(`${user}_authData`); if (!authData) return false; // Verify the auth data is valid JSON and has required fields try { const parsed = JSON.parse(authData); return parsed.challenge && parsed.signature && parsed.timestamp; } catch (e) { console.warn(`Invalid auth data for user ${user}:`, e); return false; } }); setExistingUsers(validUsers); // If there are valid users, suggest the first one for login if (validUsers.length > 0) { setSuggestedUsername(validUsers[0]); setUsername(validUsers[0]); // Pre-fill the username field setIsRegistering(false); // Default to login mode if users exist } else { setIsRegistering(true); // Default to registration mode if no users exist } // Log for debugging if (users.length !== validUsers.length) { console.log(`Found ${users.length} registered users, but only ${validUsers.length} have valid keys`); } } catch (error) { console.error('Error checking existing users:', error); setExistingUsers([]); } }; checkSupport(); checkExistingUsers(); }, [addNotification]); /** * Handle form submission for both login and registration */ const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setError(null); setIsLoading(true); try { if (!browserSupport.supported || !browserSupport.secure || !browserSupport.webcrypto) { setError('Browser does not support cryptographic authentication'); setIsLoading(false); return; } if (isRegistering) { // Registration flow using CryptoAuthService const result = await CryptoAuthService.register(username); if (result.success && result.session) { setSession(result.session); if (onSuccess) onSuccess(); } else { setError(result.error || 'Registration failed'); addNotification('Registration failed. Please try again.', 'error'); } } else { // Login flow using CryptoAuthService const result = await CryptoAuthService.login(username); if (result.success && result.session) { setSession(result.session); if (onSuccess) onSuccess(); } else { setError(result.error || 'User not found or authentication failed'); addNotification('Login failed. Please check your username.', 'error'); } } } catch (err) { console.error('Cryptographic authentication error:', err); setError('An unexpected error occurred during authentication'); addNotification('Authentication error. Please try again later.', 'error'); } finally { setIsLoading(false); } }; if (!browserSupport.supported) { return (

Browser Not Supported

Your browser does not support the required features for cryptographic authentication.

Please use a modern browser with WebCryptoAPI support.

{onCancel && ( )}
); } if (!browserSupport.secure) { return (

Secure Context Required

Cryptographic authentication requires a secure context (HTTPS).

Please access this application over HTTPS.

{onCancel && ( )}
); } return (

{isRegistering ? 'Create CryptID Account' : 'CryptID Sign In'}

{/* Show existing users if available */} {existingUsers.length > 0 && !isRegistering && (

Available Accounts with Valid Keys

{existingUsers.map((user) => ( ))}
)}

{isRegistering ? 'Create a new CryptID account using WebCryptoAPI for secure authentication.' : existingUsers.length > 0 ? 'Select an account above or enter a different username to sign in.' : 'Sign in using your CryptID credentials.' }

✓ ECDSA P-256 Key Pairs ✓ Challenge-Response Authentication ✓ Secure Key Storage
setUsername(e.target.value)} placeholder={existingUsers.length > 0 ? "Enter username or select from above" : "Enter username"} required disabled={isLoading} autoComplete="username" minLength={3} maxLength={20} />
{error &&
{error}
}
{onCancel && ( )}
); }; export default CryptID;