@nevis-security/nevis-mobile-authentication-sdk-react
Version:
React Native plugin for Nevis Mobile Authentication SDK. Supports only mobile.
363 lines (332 loc) • 13.3 kB
text/typescript
/**
* Copyright © 2023-2024 Nevis Security AG. All rights reserved.
*/
import uuid from 'react-native-uuid';
import { HttpOperation, HttpOperationImpl } from './HttpOperation';
import type { RetryPolicy } from './RetryPolicy';
import type { PasswordUserVerifier } from './userverification/PasswordUserVerifier';
import { PinUserVerifier } from './userverification/PinUserVerifier';
import type { AuthorizationProvider } from '../authorization/AuthorizationProvider';
import type { SessionProvider } from '../authorization/SessionProvider';
import { UserInteractionPlatformOperationImpl } from '../cache/operation/UserInteractionPlatformOperation';
import { PlatformOperationCache } from '../cache/PlatformOperationCache';
import { AuthenticationError } from '../error/authentication/AuthenticationError';
import { AuthenticationErrorConverter } from '../error/authentication/AuthenticationErrorConverter';
import { NativeEventListener } from '../event/NativeEventListener';
import NevisMobileAuthenticationSdkReact from '../MobileAuthenticationSdk';
import { OnSuccessMessage } from '../model/messages/in/OnSuccessMessage';
import { AuthenticationMessage } from '../model/messages/out/AuthenticationMessage';
import { AuthenticatorSelector } from '../operations/selection/AuthenticatorSelector';
import { BiometricUserVerifier } from '../operations/userverification/BiometricUserVerifier';
import { DevicePasscodeUserVerifier } from '../operations/userverification/DevicePasscodeUserVerifier';
import { FingerprintUserVerifier } from '../operations/userverification/FingerprintUserVerifier';
/**
* The object that can be used to trigger an authentication operation.
*
* Usage example:
* ```ts
* class AuthenticatorSelectorImpl extends AuthenticatorSelector {
* async selectAuthenticator(
* context: AuthenticatorSelectionContext,
* handler: AuthenticatorSelectionHandler,
* ): Promise<void> {
* await handler.aaid(aaid).catch(console.error);
* }
* }
*
* class PinUserVerifierImpl extends PinUserVerifier {
* async verifyPin(
* context: PinUserVerificationContext,
* handler: PinUserVerificationHandler,
* ): Promise<void> {
* await handler.verifyPin(pin).catch(console.error);
* }
* }
*
* class PasswordUserVerifierImpl extends PasswordUserVerifier {
* async verifyPassword(
* context: PasswordUserVerificationContext,
* handler: PasswordUserVerificationHandler,
* ): Promise<void> {
* await handler.verifyPassword(password).catch(console.error);
* }
* }
*
* class BiometricUserVerifierImpl implements BiometricUserVerifier {
* async verifyBiometric(
* context: BiometricUserVerificationContext,
* handler: BiometricUserVerificationHandler,
* ): Promise<void> {
* await handler.verifyBiometric().catch(console.error);
* }
* }
*
* [...]
* async function authenticate(
* client: MobileAuthenticationClient,
* username: string,
* sessionProvider?: SessionProvider,
* ): Promise<void> {
* await client.operations.authentication
* .username(username)
* .sessionProvider(sessionProvider)
* .authenticatorSelector(AuthenticatorSelectorImpl(...))
* .pinUserVerifier(PinUserVerifierImpl(...))
* .passwordUserVerifier(PasswordUserVerifierImpl(...))
* .biometricUserVerifier(BiometricUserVerifierImpl(...))
* .onSuccess((authorizationProvider) {
* // handle success
* })
* .onError((error) {
* // handle error
* })
* .execute();
* }
* [...]
* ```
*/
export abstract class Authentication extends HttpOperation<Authentication> {
/**
* Specifies the username that must be used to authenticate.
*
* **IMPORTANT** \
* Providing the {@link username} is required.
*
* **WARNING** \
* The username is the technical user identifier stored in the {@link Account.username} property.
* Do not provide the login identifier (for example the users e-mail address) here.
* We recommend always using the username provided via {@link LocalData.accounts}.
*
* @param username the username.
* @returns the {@link Authentication} object.
*/
abstract username(username: string): Authentication;
/**
* Specifies the session provider that must be used to authenticate.
*
* @param sessionProvider the {@link SessionProvider}.
* @returns the {@link Authentication} object.
*/
abstract sessionProvider(sessionProvider: SessionProvider): Authentication;
/**
* The retry policy to be used to obtain an {@link AuthorizationProvider} after the
* user authenticates successfully. If obtaining an {@link AuthorizationProvider}
* fails on the first try, the SDK will retry according to the provided {@link RetryPolicy}.
* This policy is used when the backend is the Identity Suite and cookies are
* created after a successful authentication.
*
* @param retryPolicy the retry policy to be used when retrieving the {@link AuthorizationProvider}.
* By default, the code will retry 3 times with a time interval of 1 second
* between tries.
* @returns the {@link Authentication} object.
*/
abstract retryPolicyObtainingAuthorizationProvider(retryPolicy: RetryPolicy): Authentication;
/**
* Specifies the object that will take care of the selection of the authenticator
* to be used.
*
* **IMPORTANT** \
* Providing the authenticator selector is required.
*
* @param authenticatorSelector the {@link AuthenticatorSelector}.
* @returns the {@link Authentication} object.
*/
abstract authenticatorSelector(authenticatorSelector: AuthenticatorSelector): Authentication;
/**
* Specifies the object that will take care of the PIN user verification.
*
* **IMPORTANT** \
* Providing at least one of the {@link PinUserVerifier}, {@link PasswordUserVerifier},
* {@link BiometricUserVerifier} or {@link DevicePasscodeUserVerifier} or {@link FingerprintUserVerifier}
* is required.
*
* @param pinUserVerifier the {@link PinUserVerifier}.
* @returns the {@link Authentication} object.
*/
abstract pinUserVerifier(pinUserVerifier: PinUserVerifier): Authentication;
/**
* Specifies the object that will take care of the password user verification.
*
* **IMPORTANT** \
* Providing at least one of the {@link PinUserVerifier}, {@link PasswordUserVerifier},
* {@link BiometricUserVerifier} or {@link DevicePasscodeUserVerifier} or {@link FingerprintUserVerifier}
* is required.
*
* @param passwordUserVerifier the {@link PasswordUserVerifier}.
* @returns the {@link Authentication} object.
*/
abstract passwordUserVerifier(passwordUserVerifier: PasswordUserVerifier): Authentication;
/**
* Specifies the object that will take care of the biometric user verification.
*
* **IMPORTANT** \
* Providing at least one of the {@link PinUserVerifier}, {@link PasswordUserVerifier},
* {@link BiometricUserVerifier} or {@link DevicePasscodeUserVerifier} or {@link FingerprintUserVerifier}
* is required.
*
* @param biometricUserVerifier the {@link BiometricUserVerifier}.
* @returns the {@link Authentication} object.
*/
abstract biometricUserVerifier(biometricUserVerifier: BiometricUserVerifier): Authentication;
/**
* Specifies the object that will take care of the device passcode user verification.
*
* **IMPORTANT** \
* Providing at least one of the {@link PinUserVerifier}, {@link PasswordUserVerifier},
* {@link BiometricUserVerifier} or {@link DevicePasscodeUserVerifier} or {@link FingerprintUserVerifier}
* is required.
*
* @param devicePasscodeUserVerifier the {@link DevicePasscodeUserVerifier}.
* @returns the {@link Authentication} object.
*/
abstract devicePasscodeUserVerifier(
devicePasscodeUserVerifier: DevicePasscodeUserVerifier
): Authentication;
/**
* Specifies the object that will take care of the fingerprint user verification.
*
* **IMPORTANT** \
* Providing at least one of the {@link PinUserVerifier}, {@link PasswordUserVerifier},
* {@link BiometricUserVerifier} or {@link DevicePasscodeUserVerifier} or {@link FingerprintUserVerifier}
* is required.
*
* @param fingerprintUserVerifier the {@link FingerprintUserVerifier}.
* @returns the {@link Authentication} object.
*/
abstract fingerprintUserVerifier(
fingerprintUserVerifier: FingerprintUserVerifier
): Authentication;
/**
* Specifies the object that will be invoked if the authentication was successful.
*
* **IMPORTANT** \
* Providing the {@link onSuccess} is required.
*
* @param onSuccess the callback which receives an optional {@link AuthorizationProvider}.
* @returns the {@link Authentication} object.
*/
abstract onSuccess(
onSuccess: (authorizationProvider?: AuthorizationProvider) => void
): Authentication;
/**
* Specifies the object that will be invoked if the authentication failed.
*
* **IMPORTANT** \
* Providing the {@link onError} is required.
*
* @param onError the function which receives an {@link AuthenticationError}.
* @returns the {@link Authentication} object.
*/
abstract onError(onError: (error: AuthenticationError) => void): Authentication;
}
export class AuthenticationImpl
extends HttpOperationImpl<Authentication>
implements Authentication
{
private _username?: string;
private _sessionProvider?: SessionProvider;
private _retryPolicyObtainingAuthorizationProvider?: RetryPolicy;
private _authenticatorSelector?: AuthenticatorSelector;
private _pinUserVerifier?: PinUserVerifier;
private _passwordUserVerifier?: PasswordUserVerifier;
private _biometricUserVerifier?: BiometricUserVerifier;
private _devicePasscodeUserVerifier?: DevicePasscodeUserVerifier;
private _fingerprintUserVerifier?: FingerprintUserVerifier;
private _onSuccess?: (authorizationProvider?: AuthorizationProvider) => void;
private _onError?: (error: AuthenticationError) => void;
username(username: string): Authentication {
this._username = username;
return this;
}
sessionProvider(sessionProvider: SessionProvider) {
this._sessionProvider = sessionProvider;
return this;
}
retryPolicyObtainingAuthorizationProvider(retryPolicy: RetryPolicy): Authentication {
this._retryPolicyObtainingAuthorizationProvider = retryPolicy;
return this;
}
authenticatorSelector(authenticatorSelector: AuthenticatorSelector): Authentication {
this._authenticatorSelector = authenticatorSelector;
return this;
}
pinUserVerifier(pinUserVerifier: PinUserVerifier): Authentication {
this._pinUserVerifier = pinUserVerifier;
return this;
}
passwordUserVerifier(passwordUserVerifier: PasswordUserVerifier): Authentication {
this._passwordUserVerifier = passwordUserVerifier;
return this;
}
biometricUserVerifier(biometricUserVerifier: BiometricUserVerifier): Authentication {
this._biometricUserVerifier = biometricUserVerifier;
return this;
}
devicePasscodeUserVerifier(
devicePasscodeUserVerifier: DevicePasscodeUserVerifier
): Authentication {
this._devicePasscodeUserVerifier = devicePasscodeUserVerifier;
return this;
}
fingerprintUserVerifier(fingerprintUserVerifier: FingerprintUserVerifier): Authentication {
this._fingerprintUserVerifier = fingerprintUserVerifier;
return this;
}
onSuccess(onSuccess: (authorizationProvider?: AuthorizationProvider) => void): Authentication {
this._onSuccess = onSuccess;
return this;
}
onError(onError: (error: AuthenticationError) => void): Authentication {
this._onError = onError;
return this;
}
async execute(): Promise<void> {
const operationId = uuid.v4() as string;
const operation = new UserInteractionPlatformOperationImpl(
operationId,
undefined,
this._authenticatorSelector,
undefined,
undefined,
undefined,
undefined,
this._pinUserVerifier,
this._passwordUserVerifier,
this._biometricUserVerifier,
this._devicePasscodeUserVerifier,
this._fingerprintUserVerifier
);
PlatformOperationCache.getInstance().put(operation);
NativeEventListener.getInstance().start(operationId);
const message = new AuthenticationMessage(
operationId,
this._authenticatorSelector !== undefined,
this._pinUserVerifier !== undefined,
this._passwordUserVerifier !== undefined,
this._biometricUserVerifier !== undefined,
this._devicePasscodeUserVerifier !== undefined,
this._fingerprintUserVerifier !== undefined,
this._onSuccess !== undefined,
this._onError !== undefined,
this._username,
this._sessionProvider,
this._retryPolicyObtainingAuthorizationProvider,
this.httpRequestHeaders
);
function finish() {
NativeEventListener.getInstance().stop(operationId);
PlatformOperationCache.getInstance().delete(operationId);
}
return NevisMobileAuthenticationSdkReact.authenticate(message)
.then((result: OnSuccessMessage) => {
finish();
const successMessage = OnSuccessMessage.fromJson(result);
this._onSuccess?.(successMessage.authorizationProvider);
})
.catch((error: Error) => {
finish();
const authenticationError = new AuthenticationErrorConverter(error).convert();
this._onError?.(authenticationError);
});
}
}