UNPKG

@nevis-security/nevis-mobile-authentication-sdk-react

Version:

React Native plugin for Nevis Mobile Authentication SDK. Supports only mobile.

355 lines (310 loc) 11.6 kB
/** * Copyright © 2023-2024 Nevis Security AG. All rights reserved. */ import { type EmitterSubscription, NativeEventEmitter, NativeModules, Platform, } from 'react-native'; import { UserInteractionPlatformOperation } from '../cache/operation/UserInteractionPlatformOperation'; import { PlatformOperationCache } from '../cache/PlatformOperationCache'; import { AuthenticatorExtension } from '../extensions/AuthenticatorExtension'; import { OnValidCredentialsProvidedMessage } from '../model/messages/in/OnValidCredentialsProvidedMessage'; import { PasswordChangerMessage } from '../model/messages/in/PasswordChangerMessage'; import { PasswordEnrollerMessage } from '../model/messages/in/PasswordEnrollerMessage'; import { PasswordValidationMessage } from '../model/messages/in/PasswordValidationMessage'; import { PinChangerMessage } from '../model/messages/in/PinChangerMessage'; import { PinEnrollerMessage } from '../model/messages/in/PinEnrollerMessage'; import { PinValidationMessage } from '../model/messages/in/PinValidationMessage'; import { SelectAccountMessage } from '../model/messages/in/SelectAccountMessage'; import { SelectAuthenticatorMessage } from '../model/messages/in/SelectAuthenticatorMessage'; import { VerifyUserMessage } from '../model/messages/in/VerifyUserMessage'; enum EventType { SelectAccount = 'selectAccount', SelectAuthenticator = 'selectAuthenticator', PinEnroll = 'pinEnroll', PasswordEnroll = 'passwordEnroll', PinChange = 'pinChange', PasswordChange = 'passwordChange', PinValidateForEnrollment = 'pinValidateForEnrollment', PasswordValidateForEnrollment = 'passwordValidateForEnrollment', PinValidateForPinChange = 'pinValidateForPinChange', PasswordValidateForPasswordChange = 'passwordValidateForPasswordChange', VerifyUser = 'verifyUser', OnValidCredentialsProvided = 'onValidCredentialsProvided', } export class NativeEventListener { private static instance: NativeEventListener; private _ongoingOperations = new Set<string>(); private _eventEmitter: NativeEventEmitter; private _selectAccount?: EmitterSubscription; private _selectAuthenticator?: EmitterSubscription; private _pinEnroll?: EmitterSubscription; private _passwordEnroll?: EmitterSubscription; private _pinChange?: EmitterSubscription; private _passwordChange?: EmitterSubscription; private _pinValidateForEnrollment?: EmitterSubscription; private _passwordValidateForEnrollment?: EmitterSubscription; private _pinValidateForPinChange?: EmitterSubscription; private _passwordValidateForPasswordChange?: EmitterSubscription; private _verifyUser?: EmitterSubscription; private _onValidCredentialsProvided?: EmitterSubscription; private constructor() { this._eventEmitter = new NativeEventEmitter(NativeModules.RNEventEmitter); } static getInstance(): NativeEventListener { if (!NativeEventListener.instance) { NativeEventListener.instance = new NativeEventListener(); } return NativeEventListener.instance; } start(operationId: string): void { this._ongoingOperations.add(operationId); this.startListeningForEvents(); } stop(operationId: string): void { this._ongoingOperations.delete(operationId); if (this._ongoingOperations.size == 0) { this.stopListeningForEvents(); } } private startListeningForEvents() { this.listenToSelectAccount(); this.listenToSelectAuthenticator(); this.listenToPinEnroll(); this.listenToPasswordEnroll(); this.listenToPinChange(); this.listenToPasswordChange(); this.listenToPinValidateForEnrollment(); this.listenToPasswordValidateForEnrollment(); this.listenToPinValidateForPinChange(); this.listenToPasswordValidateForPasswordChange(); this.listenToVerifyUser(); this.listenToOnValidCredentialsProvided(); } private stopListeningForEvents() { this._selectAccount?.remove(); this._selectAccount = undefined; this._selectAuthenticator?.remove(); this._selectAuthenticator = undefined; this._pinEnroll?.remove(); this._pinEnroll = undefined; this._passwordEnroll?.remove(); this._passwordEnroll = undefined; this._pinChange?.remove(); this._pinChange = undefined; this._passwordChange?.remove(); this._passwordChange = undefined; this._pinValidateForEnrollment?.remove(); this._pinValidateForEnrollment = undefined; this._passwordValidateForEnrollment?.remove(); this._passwordValidateForEnrollment = undefined; this._pinValidateForPinChange?.remove(); this._pinValidateForPinChange = undefined; this._passwordValidateForPasswordChange?.remove(); this._passwordValidateForPasswordChange = undefined; this._verifyUser?.remove(); this._verifyUser = undefined; this._onValidCredentialsProvided?.remove(); this._onValidCredentialsProvided = undefined; } private listenToSelectAccount(): void { if (this._selectAccount) { return; // Already listening } this._selectAccount = this._eventEmitter.addListener(EventType.SelectAccount, (data) => { const message = SelectAccountMessage.fromJson(data); const operation = this.getUserInteractionPlatformOperation( message.operationId, 'Operation not found during account selection.' ); operation.selectAccount(message.context); }); } private listenToSelectAuthenticator(): void { if (this._selectAuthenticator) { return; // Already listening } this._selectAuthenticator = this._eventEmitter.addListener( EventType.SelectAuthenticator, (data) => { const message = SelectAuthenticatorMessage.fromJson(data); const operation = this.getUserInteractionPlatformOperation( message.operationId, 'Operation not found during authenticator selection.' ); operation.selectAuthenticator(message.context); } ); } private listenToPinEnroll(): void { if (this._pinEnroll) { return; // Already listening } this._pinEnroll = this._eventEmitter.addListener(EventType.PinEnroll, (data) => { const message = PinEnrollerMessage.fromJson(data); const operation = this.getUserInteractionPlatformOperation( message.operationId, 'Operation not found during PIN enrollment.' ); operation.enrollPin(message.context); }); } private listenToPasswordEnroll(): void { if (this._passwordEnroll) { return; // Already listening } this._passwordEnroll = this._eventEmitter.addListener(EventType.PasswordEnroll, (data) => { const message = PasswordEnrollerMessage.fromJson(data); const operation = this.getUserInteractionPlatformOperation( message.operationId, 'Operation not found during password enrollment.' ); operation.enrollPassword(message.context); }); } private listenToPinChange(): void { if (this._pinChange) { return; // Already listening } this._pinChange = this._eventEmitter.addListener(EventType.PinChange, (data) => { const message = PinChangerMessage.fromJson(data); const operation = this.getUserInteractionPlatformOperation( message.operationId, 'Operation not found during PIN change.' ); operation.changePin(message.context); }); } private listenToPasswordChange(): void { if (this._passwordChange) { return; // Already listening } this._passwordChange = this._eventEmitter.addListener(EventType.PasswordChange, (data) => { const message = PasswordChangerMessage.fromJson(data); const operation = this.getUserInteractionPlatformOperation( message.operationId, 'Operation not found during password change.' ); operation.changePassword(message.context); }); } private listenToPinValidateForEnrollment(): void { if (this._pinValidateForEnrollment) { return; // Already listening } this._pinValidateForEnrollment = this._eventEmitter.addListener( EventType.PinValidateForEnrollment, (data) => { const message = PinValidationMessage.fromJson(data); const operation = this.getUserInteractionPlatformOperation( message.operationId, 'Operation not found during PIN validation for enrollment.' ); operation.validatePinForEnrollment(message.pin); } ); } private listenToPasswordValidateForEnrollment(): void { if (this._passwordValidateForEnrollment) { return; // Already listening } this._passwordValidateForEnrollment = this._eventEmitter.addListener( EventType.PasswordValidateForEnrollment, (data) => { const message = PasswordValidationMessage.fromJson(data); const operation = this.getUserInteractionPlatformOperation( message.operationId, 'Operation not found during password validation for enrollment.' ); operation.validatePasswordForEnrollment(message.password); } ); } private listenToPinValidateForPinChange(): void { if (this._pinValidateForPinChange) { return; // Already listening } this._pinValidateForPinChange = this._eventEmitter.addListener( EventType.PinValidateForPinChange, (data) => { const message = PinValidationMessage.fromJson(data); const operation = this.getUserInteractionPlatformOperation( message.operationId, 'Operation not found during PIN validation for PIN change.' ); operation.validatePinForPinChange(message.pin); } ); } private listenToPasswordValidateForPasswordChange(): void { if (this._passwordValidateForPasswordChange) { return; // Already listening } this._passwordValidateForPasswordChange = this._eventEmitter.addListener( EventType.PasswordValidateForPasswordChange, (data) => { const message = PasswordValidationMessage.fromJson(data); const operation = this.getUserInteractionPlatformOperation( message.operationId, 'Operation not found during password validation for password change.' ); operation.validatePasswordForPasswordChange(message.password); } ); } private listenToVerifyUser(): void { if (this._verifyUser) { return; // Already listening } this._verifyUser = this._eventEmitter.addListener(EventType.VerifyUser, (data) => { const message = VerifyUserMessage.fromJson(data); const operation = this.getUserInteractionPlatformOperation( message.operationId, 'Operation not found during user verification.' ); const handler = AuthenticatorExtension.handlerByAuthenticator( message.context.authenticator.aaid, message.operationId ); operation.userVerificationHandler = handler; PlatformOperationCache.getInstance().update(operation); (async () => { await operation.verifyUser(message.context, handler); })(); }); } private listenToOnValidCredentialsProvided(): void { // listenToOnValidCredentialsProvided is only supported on an Android platform if (Platform.OS !== 'android') { return; } if (this._onValidCredentialsProvided) { return; // Already listening } this._onValidCredentialsProvided = this._eventEmitter.addListener( EventType.OnValidCredentialsProvided, (data) => { const message = OnValidCredentialsProvidedMessage.fromJson(data); const operation = this.getUserInteractionPlatformOperation( message.operationId, 'Operation not found during providing valid credentials.' ); operation.userVerificationHandler = undefined; PlatformOperationCache.getInstance().update(operation); operation.onValidCredentialsProvided(message.authenticator); } ); } private getUserInteractionPlatformOperation( operationId: string, errorMessage: string ): UserInteractionPlatformOperation { const operation = PlatformOperationCache.getInstance().read(operationId); if (operation instanceof UserInteractionPlatformOperation) { return operation; } throw new Error(errorMessage); } }