UNPKG

@ideem/zsm-react-native

Version:

ZSM makes 2FA easy and invisible for everyone, all the time, using advanced cryptography like MPC to establish cryptographic proof of the origin of any transaction or login attempt, while eliminating opportunities for social engineering. ZSM has no relian

148 lines (130 loc) 6.46 kB
import WebAuthnClient from './webauthn-client'; import ZSMLogger from './zsm-logger'; class UMFAClient { #zsmAPI; static get version() { return WebAuthnClient.version; } constructor(config) { window.zsm = {}; this.config = config; this.#zsmAPI = new WebAuthnClient(config); this.checkEnrollment = this.checkEnrollment.bind(this); this.enroll = this.enroll.bind(this); this.authenticate = this.authenticate.bind(this); this.resetDevice = this.resetDevice.bind(this); } get userIdentifier() { return this.#zsmAPI.userIdentifier; } get credentialID() { return this.#zsmAPI.credentialID; } checkEnrollment = async (userIdentifier=this.userIdentifier) => { const traceId = ZSMLogger.generateTraceId(); try { ZSMLogger.debug(`checkEnrollment called with userIdentifier: ${userIdentifier}`, traceId); // Initialize ZSM before checking credentials (like authenticate/enroll methods) await this.#zsmAPI.initializeZsm(userIdentifier); // Simple check like web SDK - just check if credentials exist let enrollmentDetails = await this.#zsmAPI.webauthnRetrieve(userIdentifier, false, traceId); // Return true if credential exists, false otherwise return !!enrollmentDetails; } catch(e) { ZSMLogger.debug(`checkEnrollment failed: ${e.message}`, traceId); // Return false like web SDK when not enrolled return false; } } enroll = async (userIdentifier = this.userIdentifier) => { const traceId = ZSMLogger.generateTraceId(); try { ZSMLogger.debug(`enroll called with userIdentifier: ${userIdentifier}`, traceId); let userIsEnrolled = await this.checkEnrollment(userIdentifier); if(userIsEnrolled !== false) return false; let creationResult = await this.#zsmAPI.webauthnCreate(userIdentifier, traceId); if(creationResult instanceof Error) throw(`Unable to enroll ${userIdentifier} during the creation process!`); return creationResult?.token || creationResult; }catch(e) { e = e.message || e; e = 'Unable to complete enrollment: ' + e; ZSMLogger.trace(`enroll failed: ${e}`); return new Error(e); } } authenticate = async (userIdentifier = this.userIdentifier) => { const traceId = ZSMLogger.generateTraceId(); try { ZSMLogger.debug(`authenticate called with userIdentifier: ${userIdentifier}`, traceId); // Only call checkIdentity if we don't already have an identity_id // If user was enrolled with optimized flow, identity_id should already be stored if (!this.#zsmAPI.identityId) { ZSMLogger.debug(`Getting identity for authentication: ${userIdentifier}`, traceId); await this.#zsmAPI.checkIdentity(userIdentifier, false); } else { ZSMLogger.debug(`Using existing identity_id for authentication: ${this.#zsmAPI.identityId}`, traceId); } let userIsEnrolled = await this.checkEnrollment(userIdentifier); if(userIsEnrolled === false) throw(`${userIdentifier} is not enrolled.`); let authCredential = await this.#zsmAPI.webauthnPartialGet(userIdentifier, traceId); // Return full credential object to match web implementation return authCredential; }catch(e) { e = e.message || e; e = 'Unable to complete authentication: ' + e; ZSMLogger.trace(`authenticate failed: ${e}`); return new Error(e); } } resetDevice = async () => { this.#zsmAPI.webauthnReset(); } /** * Performs a comprehensive health check on all UMFA services and configuration. * @returns {Promise<Object>} Resolves with detailed health status for UMFA functionality. */ healthCheck = async () => { try { const traceId = ZSMLogger.generateTraceId(); ZSMLogger.info('Starting UMFA health check...', traceId); // Get base health check from WebAuthnClient const baseHealth = await this.#zsmAPI.healthCheck(); // Add UMFA-specific information const umfaHealth = { ...baseHealth, umfa_client: { version: this.version, config: this.config, current_user: this.userIdentifier || 'NOT_SET', credential_id: this.credentialID || 'NOT_SET', enrollment_status: this.credentialID ? 'ENROLLED' : 'NOT_ENROLLED' } }; // Test UMFA-specific functionality if user is set if (this.userIdentifier) { ZSMLogger.trace('Testing UMFA enrollment check...', traceId); try { const enrollmentResult = await this.checkEnrollment(this.userIdentifier); umfaHealth.umfa_client.enrollment_test = { status: enrollmentResult instanceof Error ? 'FAILED' : 'OK', result: enrollmentResult instanceof Error ? enrollmentResult.message : 'User enrollment verified' }; } catch (error) { umfaHealth.umfa_client.enrollment_test = { status: 'ERROR', error: error.message }; } } ZSMLogger.info(`UMFA health check complete: ${umfaHealth.overall_status}`, traceId); ZSMLogger.trace(`Full UMFA health check result: ${JSON.stringify(umfaHealth)}`, traceId); return umfaHealth; } catch (error) { const errorResult = { timestamp: new Date().toISOString(), overall_status: 'ERROR', umfa_client: { version: this.version, error: error.message } }; ZSMLogger.trace(`UMFA health check failed: ${error.message}`); return errorResult; } } } export default UMFAClient;