@nevis-security/nevis-mobile-authentication-sdk-react
Version:
React Native plugin for Nevis Mobile Authentication SDK. Supports only mobile.
533 lines (498 loc) • 19.5 kB
text/typescript
/**
* Copyright © 2023-2024 Nevis Security AG. All rights reserved.
*/
import { PlatformOperation } from './PlatformOperation';
import { Aaid } from '../../localData/Aaid';
import { Authenticator } from '../../localData/Authenticator';
import NevisMobileAuthenticationSdkReact from '../../MobileAuthenticationSdk';
import { PasswordValidatedMessage } from '../../model/messages/out/PasswordValidatedMessage';
import { PinValidatedMessage } from '../../model/messages/out/PinValidatedMessage';
import type { PasswordChangeContext } from '../../operations/password/PasswordChangeContext';
import {
PasswordChangeHandler,
PasswordChangeHandlerImpl,
} from '../../operations/password/PasswordChangeHandler';
import type { PasswordChanger } from '../../operations/password/PasswordChanger';
import type { PasswordEnroller } from '../../operations/password/PasswordEnroller';
import type { PasswordEnrollmentContext } from '../../operations/password/PasswordEnrollmentContext';
import {
PasswordEnrollmentHandler,
PasswordEnrollmentHandlerImpl,
} from '../../operations/password/PasswordEnrollmentHandler';
import { PinChangeContext } from '../../operations/pin/PinChangeContext';
import { PinChangeHandler, PinChangeHandlerImpl } from '../../operations/pin/PinChangeHandler';
import { PinChanger } from '../../operations/pin/PinChanger';
import { PinEnroller } from '../../operations/pin/PinEnroller';
import type { PinEnrollmentContext } from '../../operations/pin/PinEnrollmentContext';
import {
PinEnrollmentHandler,
PinEnrollmentHandlerImpl,
} from '../../operations/pin/PinEnrollmentHandler';
import type { AccountSelectionContext } from '../../operations/selection/AccountSelectionContext';
import {
AccountSelectionHandler,
AccountSelectionHandlerImpl,
} from '../../operations/selection/AccountSelectionHandler';
import { AccountSelector } from '../../operations/selection/AccountSelector';
import type { AuthenticatorSelectionContext } from '../../operations/selection/AuthenticatorSelectionContext';
import {
AuthenticatorSelectionHandler,
AuthenticatorSelectionHandlerImpl,
} from '../../operations/selection/AuthenticatorSelectionHandler';
import { AuthenticatorSelector } from '../../operations/selection/AuthenticatorSelector';
import type { BiometricUserVerificationHandler } from '../../operations/userverification/BiometricUserVerificationHandler';
import { BiometricUserVerifier } from '../../operations/userverification/BiometricUserVerifier';
import type { DevicePasscodeUserVerificationHandler } from '../../operations/userverification/DevicePasscodeUserVerificationHandler';
import { DevicePasscodeUserVerifier } from '../../operations/userverification/DevicePasscodeUserVerifier';
import type { FingerprintUserVerificationHandler } from '../../operations/userverification/FingerprintUserVerificationHandler';
import { FingerprintUserVerifier } from '../../operations/userverification/FingerprintUserVerifier';
import type { PasswordUserVerificationContext } from '../../operations/userverification/PasswordUserVerificationContext';
import type { PasswordUserVerificationHandler } from '../../operations/userverification/PasswordUserVerificationHandler';
import type { PasswordUserVerifier } from '../../operations/userverification/PasswordUserVerifier';
import type { PinUserVerificationContext } from '../../operations/userverification/PinUserVerificationContext';
import type { PinUserVerificationHandler } from '../../operations/userverification/PinUserVerificationHandler';
import { PinUserVerifier } from '../../operations/userverification/PinUserVerifier';
import type { UserVerificationContext } from '../../operations/userverification/UserVerificationContext';
import type { UserVerificationHandler } from '../../operations/userverification/UserVerificationHandler';
/**
* Helps in following the states of user interaction operations during method
* channel calls.
*/
export abstract class UserInteractionPlatformOperation extends PlatformOperation {
/**
* The {@link AccountSelector} given when an operation is started.
*
* E.g.: During an out-of-band process.
*/
abstract accountSelector?: AccountSelector;
/**
* The {@link AuthenticatorSelector} given when an operation is started.
*
* E.g.: During an out-of-band process.
*/
abstract authenticatorSelector?: AuthenticatorSelector;
/**
* The {@link PinEnroller} given when an operation is started.
*
* E.g.: During an out-of-band process.
*/
abstract pinEnroller?: PinEnroller;
/**
* The {@link PasswordEnroller} given when an operation is started.
*
* E.g.: During an out-of-band process.
*/
abstract passwordEnroller?: PasswordEnroller;
/**
* The {@link PinChanger} given when an operation is started.
*/
abstract pinChanger?: PinChanger;
/**
* The {@link PasswordChanger} given when an operation is started.
*/
abstract passwordChanger?: PasswordChanger;
/**
* The {@link PinUserVerifier} given when an operation is started.
*
* E.g.: During an out-of-band process.
*/
abstract pinUserVerifier?: PinUserVerifier;
/**
* The {@link PasswordUserVerifier} given when an operation is started.
*
* E.g.: During an out-of-band process.
*/
abstract passwordUserVerifier?: PasswordUserVerifier;
/**
* The {@link BiometricUserVerifier} given when an operation is started.
*
* E.g.: During an out-of-band process.
*/
abstract biometricUserVerifier?: BiometricUserVerifier;
/**
* The {@link DevicePasscodeUserVerifier} given when an operation is started.
*
* E.g.: During an out-of-band process.
*/
abstract devicePasscodeUserVerifier?: DevicePasscodeUserVerifier;
/**
* The {@link FingerprintUserVerifier} given when an operation is started.
*
* E.g.: During an out-of-band process.
*/
abstract fingerprintUserVerifier?: FingerprintUserVerifier;
/**
* The {@link AccountSelectionHandler} given when an operation is started.
*
* E.g.: During an out-of-band process.
* This is generated automatically based on the {@link operationId}.
*/
abstract accountSelectionHandler?: AccountSelectionHandler;
/**
* The {@link AuthenticatorSelectionHandler} given when an operation is started.
*
* E.g.: During an out-of-band process.
* This is generated automatically based on the {@link operationId}.
*/
abstract authenticatorSelectionHandler?: AuthenticatorSelectionHandler;
/**
* The {@link PinEnrollmentHandler} given when an operation is started.
*
* E.g.: During an out-of-band process.
* This is generated automatically based on the {@link operationId}.
*/
abstract pinEnrollmentHandler?: PinEnrollmentHandler;
/**
* The {@link PasswordEnrollmentHandler} given when an operation is started.
*
* E.g.: During an out-of-band process.
* This is generated automatically based on the {@link operationId}.
*/
abstract passwordEnrollmentHandler?: PasswordEnrollmentHandler;
/**
* The {@link PinChangeHandler} given when an operation is started.
*
* E.g.: During a pin change.
* This is generated automatically based on the {@link operationId}.
*/
abstract pinChangeHandler?: PinChangeHandler;
/**
* The {@link PasswordChangeHandler} given when an operation is started.
*
* E.g.: During a password change.
* This is generated automatically based on the {@link operationId}.
*/
abstract passwordChangeHandler?: PasswordChangeHandler;
/**
* The {@link UserVerificationHandler} given when an operation is in the state of user verification.
*/
abstract userVerificationHandler?: UserVerificationHandler;
/**
* The account selection interaction.
*
* The implementing class must ask the user to choose one of the accounts
* exposed by the {@link AccountSelectionContext} and provide the choice to the
* {@link AccountSelectionHandler}.
*
* @param context the object containing the list of existing accounts and authenticators.
*/
selectAccount(context: AccountSelectionContext): void {
this.accountSelector?.selectAccount(context, this.accountSelectionHandler!);
}
/**
* The authenticator selection interaction.
*
* The implementing class must ask the user to choose one of the authenticators
* exposed by the {@link AuthenticatorSelectionContext} and provide the choice to the
* {@link AuthenticatorSelectionHandler}.
*
* Note, that in the case of transaction confirmation (which can be considered
* a special case of authentication) the implementing classes must present
* the contents of the transaction (if any) to the user for verification
* @see {@link AuthenticatorSelectionContext.transactionConfirmationData}
*
* @param context the object containing the list of existing authenticators.
*/
selectAuthenticator(context: AuthenticatorSelectionContext): void {
this.authenticatorSelector?.selectAuthenticator(
context,
this.authenticatorSelectionHandler!
);
}
/**
* The method that will be invoked till either the user provides a PIN that
* conforms with the format specified by the {@link PinPolicy} or till the
* operation is cancelled (through the {@link PinEnrollmentHandler.cancel}).
*
* @param context the context.
*/
enrollPin(context: PinEnrollmentContext): void {
this.pinEnroller?.enrollPin(context, this.pinEnrollmentHandler!);
}
/**
* The method that will be invoked till either the user provides a password that
* conforms with the format specified by the {@link PasswordPolicy} or till the
* operation is cancelled (through the {@link PasswordEnrollmentHandler.cancel}).
*
* @param context the context.
*/
enrollPassword(context: PasswordEnrollmentContext): void {
this.passwordEnroller?.enrollPassword(context, this.passwordEnrollmentHandler!);
}
/**
* The method that will be invoked till either the user provides the old PIN
* and a new PIN that conforms with the format specified by the {@link PinPolicy},
* or till the operation is cancelled (through the {@link PinChangeHandler.cancel}),
* or till the PIN authenticator is permanently locked because the user provided
* too many times an invalid PIN.
*
* @param context the context.
*/
changePin(context: PinChangeContext) {
this.pinChanger?.changePin(context, this.pinChangeHandler!);
}
/**
* The method that will be invoked till either the user provides the old password
* and a new password that conforms with the format specified by the {@link PasswordPolicy},
* or till the operation is cancelled (through the {@link PasswordChangeHandler.cancel}),
* or till the password authenticator is permanently locked because the user provided
* too many times an invalid password.
*
* @param context the context.
*/
changePassword(context: PasswordChangeContext) {
this.passwordChanger?.changePassword(context, this.passwordChangeHandler!);
}
/**
* The user verification interaction.
*
* In the case of the registration the user must provide credentials again as
* required by the FIDO UAF protocol.
* In the case of the authentication, this is invoked for the user to provide
* credentials.
*
* If the user provided invalid credentials, and it results in a non-recoverable
* error, then `onSuccess` method will be invoked.
*
* @param context the object providing the information required for the verification
* process.
* @param handler the object that must be notified with the result of the interaction.
*/
verifyUser(context: UserVerificationContext, handler: UserVerificationHandler): Promise<void> {
switch (context.authenticator.aaid) {
case Aaid.PIN.rawValue():
return this.pinUserVerifier!.verifyPin(
context as PinUserVerificationContext,
handler as PinUserVerificationHandler
);
case Aaid.PASSWORD.rawValue():
return this.passwordUserVerifier!.verifyPassword(
context as PasswordUserVerificationContext,
handler as PasswordUserVerificationHandler
);
case Aaid.BIOMETRIC.rawValue():
return this.biometricUserVerifier!.verifyBiometric(
context,
handler as BiometricUserVerificationHandler
);
case Aaid.DEVICE_PASSCODE.rawValue():
return this.devicePasscodeUserVerifier!.verifyDevicePasscode(
context,
handler as DevicePasscodeUserVerificationHandler
);
case Aaid.FINGERPRINT.rawValue():
return this.fingerprintUserVerifier!.verifyFingerprint(
context,
handler as FingerprintUserVerificationHandler
);
}
return Promise.reject(
new Error(
`No verifier found for Authenticator aaid ${context.authenticator.aaid} when verifying the user.`
)
);
}
/**
* This method is invoked when either valid local system credentials (biometric,
* fingerprint) or valid SDK-managed credentials (PIN, password) were provided
* and verified locally.
*
* This method can be used for instance to display some progress message
* indicating that the operation is ongoing.
*
* Note that invoking this method does not mean that the UAF operation completed
* successfully (this is notified through `onSuccess` methods once the FIDO UAF
* server validates the request generated with the credentials).
*
* @param authenticator the object describing the authenticator where credentials
* were validated.
*/
onValidCredentialsProvided(authenticator: Authenticator): void {
switch (authenticator.aaid) {
case Aaid.PIN.rawValue():
return this.pinUserVerifier?.onValidCredentialsProvided();
case Aaid.PASSWORD.rawValue():
return this.passwordUserVerifier?.onValidCredentialsProvided();
case Aaid.BIOMETRIC.rawValue():
return this.biometricUserVerifier?.onValidCredentialsProvided();
case Aaid.DEVICE_PASSCODE.rawValue():
return this.devicePasscodeUserVerifier?.onValidCredentialsProvided();
case Aaid.FINGERPRINT.rawValue():
return this.fingerprintUserVerifier?.onValidCredentialsProvided();
}
throw new Error(
`No verifier found for Authenticator aaid ${authenticator.aaid} when valid credentials provided.`
);
}
/**
* Performs validation other than the minimum and maximum PIN length during PIN enrollment.
*
* @param pin the PIN to be validated.
*/
validatePinForEnrollment(pin: string) {
this.pinEnroller?.pinPolicy.validatePinForEnrollment(
pin,
() => {
(async () => {
const message = new PinValidatedMessage(this.operationId, undefined, undefined);
await NevisMobileAuthenticationSdkReact.pinValidatedForEnrollment(message);
})();
},
(error) => {
(async () => {
const message = new PinValidatedMessage(
this.operationId,
error.description,
error.cause
);
await NevisMobileAuthenticationSdkReact.pinValidatedForEnrollment(message);
})();
}
);
}
/**
* Performs validation during password enrollment.
*
* @param password the password to be validated.
*/
validatePasswordForEnrollment(password: string) {
this.passwordEnroller?.passwordPolicy.validatePasswordForEnrollment(
password,
() => {
(async () => {
const message = new PasswordValidatedMessage(
this.operationId,
undefined,
undefined
);
await NevisMobileAuthenticationSdkReact.passwordValidatedForEnrollment(message);
})();
},
(error) => {
(async () => {
const message = new PasswordValidatedMessage(
this.operationId,
error.description,
error.cause
);
await NevisMobileAuthenticationSdkReact.passwordValidatedForEnrollment(message);
})();
}
);
}
/**
* Performs validation other than the minimum and maximum PIN length during PIN change.
*
* @param pin the PIN to be validated.
*/
validatePinForPinChange(pin: string) {
this.pinChanger?.pinPolicy.validatePinForPinChange(
pin,
() => {
(async () => {
const message = new PinValidatedMessage(this.operationId, undefined, undefined);
await NevisMobileAuthenticationSdkReact.pinValidatedForPinChange(message);
})();
},
(error) => {
(async () => {
const message = new PinValidatedMessage(
this.operationId,
error.description,
error.cause
);
await NevisMobileAuthenticationSdkReact.pinValidatedForPinChange(message);
})();
}
);
}
/**
* Performs validation during password enrollment.
*
* @param password the password to be validated.
*/
validatePasswordForPasswordChange(password: string) {
this.passwordChanger?.passwordPolicy.validatePasswordForPasswordChange(
password,
() => {
(async () => {
const message = new PasswordValidatedMessage(
this.operationId,
undefined,
undefined
);
await NevisMobileAuthenticationSdkReact.passwordValidatedForPasswordChange(
message
);
})();
},
(error) => {
(async () => {
const message = new PasswordValidatedMessage(
this.operationId,
error.description,
error.cause
);
await NevisMobileAuthenticationSdkReact.passwordValidatedForPasswordChange(
message
);
})();
}
);
}
}
export class UserInteractionPlatformOperationImpl extends UserInteractionPlatformOperation {
operationId: string;
accountSelector?: AccountSelector;
accountSelectionHandler?: AccountSelectionHandler;
authenticatorSelector?: AuthenticatorSelector;
authenticatorSelectionHandler?: AuthenticatorSelectionHandler;
pinEnroller?: PinEnroller;
pinEnrollmentHandler?: PinEnrollmentHandler;
passwordEnroller?: PasswordEnroller;
passwordEnrollmentHandler?: PasswordEnrollmentHandler;
pinChanger?: PinChanger;
pinChangeHandler?: PinChangeHandler;
passwordChanger?: PasswordChanger;
passwordChangeHandler?: PasswordChangeHandler;
pinUserVerifier?: PinUserVerifier;
passwordUserVerifier?: PasswordUserVerifier;
biometricUserVerifier?: BiometricUserVerifier;
devicePasscodeUserVerifier?: DevicePasscodeUserVerifier;
fingerprintUserVerifier?: FingerprintUserVerifier;
userVerificationHandler?: UserVerificationHandler;
constructor(
operationId: string,
accountSelector?: AccountSelector,
authenticatorSelector?: AuthenticatorSelector,
pinEnroller?: PinEnroller,
passwordEnroller?: PasswordEnroller,
pinChanger?: PinChanger,
passwordChanger?: PasswordChanger,
pinUserVerifier?: PinUserVerifier,
passwordUserVerifier?: PasswordUserVerifier,
biometricUserVerifier?: BiometricUserVerifier,
devicePasscodeUserVerifier?: DevicePasscodeUserVerifier,
fingerprintUserVerifier?: FingerprintUserVerifier
) {
super();
this.operationId = operationId;
this.accountSelector = accountSelector;
this.authenticatorSelector = authenticatorSelector;
this.pinEnroller = pinEnroller;
this.passwordEnroller = passwordEnroller;
this.pinChanger = pinChanger;
this.passwordChanger = passwordChanger;
this.pinUserVerifier = pinUserVerifier;
this.passwordUserVerifier = passwordUserVerifier;
this.biometricUserVerifier = biometricUserVerifier;
this.devicePasscodeUserVerifier = devicePasscodeUserVerifier;
this.fingerprintUserVerifier = fingerprintUserVerifier;
this.accountSelectionHandler = new AccountSelectionHandlerImpl(operationId);
this.authenticatorSelectionHandler = new AuthenticatorSelectionHandlerImpl(operationId);
this.pinEnrollmentHandler = new PinEnrollmentHandlerImpl(operationId);
this.passwordEnrollmentHandler = new PasswordEnrollmentHandlerImpl(operationId);
this.pinChangeHandler = new PinChangeHandlerImpl(operationId);
this.passwordChangeHandler = new PasswordChangeHandlerImpl(operationId);
}
}