UNPKG

capacitor-biometric-authentication

Version:

Framework-agnostic biometric authentication library. Works with React, Vue, Angular, or vanilla JS. No providers required!

175 lines 6.56 kB
import { BiometricErrorCode, BiometryType } from '../core/types'; import { arrayBufferToBase64, base64ToArrayBuffer, generateSessionId as generateSecureSessionId, } from '../utils/encoding'; import { createErrorResult } from '../utils/error-handler'; export class WebAdapter { constructor() { this.platform = 'web'; this.credentials = new Map(); // Set default Relying Party info this.rpId = window.location.hostname; this.rpName = document.title || 'Biometric Authentication'; } async isAvailable() { // Check if WebAuthn is supported if (!window.PublicKeyCredential) { return false; } // Check if platform authenticator is available try { const available = await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable(); return available; } catch (_a) { return false; } } async getSupportedBiometrics() { if (!(await this.isAvailable())) { return []; } // WebAuthn doesn't provide specific biometry types // Return generic "multiple" as modern devices support various methods return [BiometryType.MULTIPLE]; } async authenticate(options) { var _a; try { // Check if WebAuthn is available if (!(await this.isAvailable())) { return { success: false, error: { code: BiometricErrorCode.BIOMETRIC_UNAVAILABLE, message: 'WebAuthn is not available on this device' } }; } const webOptions = ((_a = options === null || options === void 0 ? void 0 : options.platform) === null || _a === void 0 ? void 0 : _a.web) || {}; // Try to get existing credential first const existingCredential = await this.getExistingCredential(webOptions); if (existingCredential) { return { success: true, biometryType: BiometryType.MULTIPLE, sessionId: generateSecureSessionId(), platform: 'web' }; } // If no existing credential, create a new one const credential = await this.createCredential((options === null || options === void 0 ? void 0 : options.reason) || 'Authentication required', webOptions); if (credential) { // Store credential for future use const credentialId = arrayBufferToBase64(credential.rawId); this.credentials.set(credentialId, credential); this.saveCredentialId(credentialId); return { success: true, biometryType: BiometryType.MULTIPLE, sessionId: generateSecureSessionId(), platform: 'web' }; } return { success: false, error: { code: BiometricErrorCode.AUTHENTICATION_FAILED, message: 'Failed to authenticate' } }; } catch (error) { return createErrorResult(error); } } async deleteCredentials() { this.credentials.clear(); localStorage.removeItem('biometric_credential_ids'); } async hasCredentials() { const storedIds = this.getStoredCredentialIds(); return storedIds.length > 0; } async getExistingCredential(options) { const storedIds = this.getStoredCredentialIds(); if (storedIds.length === 0) { return null; } try { const challenge = options.challenge || crypto.getRandomValues(new Uint8Array(32)); const publicKeyOptions = { challenge, rpId: options.rpId || this.rpId, timeout: options.timeout || 60000, userVerification: options.userVerification || 'preferred', allowCredentials: storedIds.map(id => ({ id: base64ToArrayBuffer(id), type: 'public-key' })) }; const credential = await navigator.credentials.get({ publicKey: publicKeyOptions }); return credential; } catch (_a) { return null; } } async createCredential(_reason, options) { try { const challenge = options.challenge || crypto.getRandomValues(new Uint8Array(32)); const userId = crypto.getRandomValues(new Uint8Array(32)); const publicKeyOptions = { challenge, rp: { id: options.rpId || this.rpId, name: options.rpName || this.rpName }, user: { id: userId, name: 'user@' + this.rpId, displayName: 'User' }, pubKeyCredParams: [ { type: 'public-key', alg: -7 }, // ES256 { type: 'public-key', alg: -257 } // RS256 ], authenticatorSelection: options.authenticatorSelection || { authenticatorAttachment: 'platform', userVerification: 'preferred', requireResidentKey: false, residentKey: 'discouraged' }, timeout: options.timeout || 60000, attestation: options.attestation || 'none' }; const credential = await navigator.credentials.create({ publicKey: publicKeyOptions }); return credential; } catch (_a) { return null; } } getStoredCredentialIds() { const stored = localStorage.getItem('biometric_credential_ids'); if (!stored) { return []; } try { return JSON.parse(stored); } catch (_a) { return []; } } saveCredentialId(id) { const existing = this.getStoredCredentialIds(); if (!existing.includes(id)) { existing.push(id); localStorage.setItem('biometric_credential_ids', JSON.stringify(existing)); } } } //# sourceMappingURL=WebAdapter.js.map