User Interaction Delegation

Overview

Some operations performed by the SDK require user interaction, e.g. account or authenticator selection, user enrollment or user verification. While some interactions are triggered using system API (like fingerprint authorization using Touch ID), others need a custom implementation. In those cases, the SDK will delegate the screen presentation and user interaction handling to the application through the userInteractionDelegate parameter which has to be passed to the particular SDK function call. SDK components will then access and call that delegate when the current operation requires user interaction. The userInteractionDelegate needs to be passed to the SDK on a Nevis operation basis.

User interactions follow specific contracts established by the UserInteractionDelegate, AuthenticationUserInteractionDelegate, DeregistrationUserInteractionDelegate and OpenSettingsUserInteractionDelegate. Each type of interaction declares a Context which is passed by the SDK to the application in order to get the needed information for the current operation. A user interaction also expects an Outcome or an Error sent by the application back to the SDK when the interaction is completed.

Note
User Interaction methods are asynchronous, which means that the SDK will wait for a certain amount of time. After that period of time, a timeout error will automatically be returned if no outcome was provided by the application.
In case of a cool-down is in place because of the too many failed PIN attempts, the wait time is extended with the time of the cool-down.

Default user interaction timeout is 4 minutes.

Account Selection

Protocol: AuthenticationUserInteractionDelegate

In username-less scenarios the Nevis Mobile Authentication SDK calls this delegate in order to select an account to be used for the current operation. It is the application’s responsibility to decide whether to ask the user to do the selection or not. The SDK returns all accounts that have valid registration stored in the SDK.

Diagram of Account Selection

  1. The user initiates a username-less OOB authentication that requires account selection.
  2. The application calls the SDK to start the operation.
  3. The SDK calls the selectAccount(_:completion:) delegate method with a AccountSelection.Context parameter that contains the list of accounts.
  4. The Application shows the list of accounts.
  5. The user selects an account.
  6. The application sends the outcome to the SDK, which continues the operation.

Example

func selectAccount(_ context: AccountSelection.Context, completion handler: @escaping (Result<AccountSelection.Outcome>) -> Void) {
    // If there is only one account available, automatically select it and continue the process.
    if context.accounts.count == 1, let account = context.accounts.first {
        let outcome = AccountSelection.Outcome(accountUsername: account.username)
        handler(.success(outcome))
        return
    }

    // Otherwise, ask the user to select its preferred account by presenting a screen.
    showAccountSelection(with: context, completion: handler)
}

Authenticator Selection

The delegate is called by the SDK in order to select an authenticator to be used for a given operation. It is the application’s responsibility to decide whether to ask the user to do the selection or not.

The Nevis Mobile Authentication SDK returns all authenticators that the SDK itself supports. Not all of these authenticators are usable for a certain operation. It can either be, that the authenticator is not supported by the current device - this is indicated within the AuthenticatorInformation.isSupportedByHardware flag -. It is also possible that an authenticator is only not usable due to the Policy set on the backend. This can be verified for a certain authenticator by calling isPolicyCompliant(_:), provided in the AuthenticatorSelection.Context.

Diagram of Authenticator Selection

  1. The user initiates an action that requires authorization, like registration or authentication.
  2. The application calls the SDK to start the operation.
  3. The SDK calls the selectAuthenticator(_:completion:) delegate method with a AuthenticatorSelection.Context parameter that contains the list of authenticators.
  4. The application optionally filters the provided list if desired with the provided isPolicyCompliant(_:) function to filter not supported authenticators based on the Policy for the current operation.
  5. The application optionally filters the provided list if desired with the flag in AuthenticatorInformation.isSupportedByHardware to filter not supported authenticators by the device.
  6. Shows the list of filtered authenticators to the user.
  7. The user selects an authenticator.
  8. The application sends the outcome to the SDK, which continues the operation.

Example

func selectAuthenticator(_ context: AuthenticatorSelection.Context, completion handler: @escaping (Result<AuthenticatorSelection.Outcome>) -> Void) {
    // If there is only one authenticator available, automatically select it and continue the process.
    if context.authenticators.count == 1, let authenticator = context.authenticators.first {
        let outcome = AuthenticatorSelection.Outcome(aaid: aaid)
        handler(.success(outcome))
        return
    }

    // Otherwise, ask the user to select its preferred authenticator by presenting a screen.
    showAuthenticatorSelection(with: context, completion: handler)
}

User Verification

The delegate is called by the SDK during a registration or authentication to provide the application with information about user verification. The method is fired by all authenticators, but not all them require the application to provide appropriate credentials that will be validated by the authenticator; the type of user verification method, and the expected format is provided as part of the UserVerification.Context.

Note
While using system-based authenticators like Fingerprint (Touch ID) or Face Recognition (Face ID), this method is guaranteed to be triggered before the user verification process. This allows the application to display the transaction confirmation message, for example. It is the application responsibility to transform (e.g. translate to another language, format text, etc.) the transaction confirmation message sent by the relying party server before displaying it to the user.

Note
For the App PIN Authenticator a brute-force attack prevention is in place, and as a part of that, the Nevis Mobile Authentication SDK provides an AuthenticatorProtectionStatus object, containing information about current status of the PIN protection in PINAuthenticatorProtectionStatus. In case of any failed attempts the UserVerification.Context contains the reason of the last failure as a RecoverableError. This is sent in the Authentication scenario in User Verification.

Note
With the introduction of supporting multiple accounts it was required from the SDK to let the application know about the account used for the current operation. This information is provided through the Context objects.

The AuthenticatorInformation object is also extended by the information about enrolled accounts via UserEnrollments and registered accounts via Registation objects.

Diagram of User Verification

  1. The user initiates an action that requires authentication, like registration or authentication by login.
  2. The application calls the SDK to start the operation.
  3. The SDK calls the verifyUser(_:completion:) delegate method.
  4. The application asks the user to enter their credentials, if needed:
    • For a PIN user verification, the application is responsible to ask the user to enter their credentials.
    • If the user verification is handled by the SDK, the application doesn’t have to do anything.
  5. The application sends the outcome to the SDK.
  6. In case of invalid PIN credentials provided as well as PIN authenticator is not locked out by PIN brute force attack prevention, the SDK calls the verifyUser(_:completion:) delegate method again. 7-8. The application asks the user to enter their credentials again and sends the outcome to the SDK.
  7. The SDK continues the operation and sends a result to the application
  8. The application notifies the user about the operation result.

Example

Note
The usage of authenticatorProtectionStatus and lastRecoverableError are explicitly excluded from this code snippet for the sake of simplicity.

func verifyUser(_ context: UserVerification.Context, completion handler: @escaping (Result<UserVerification.Outcome>) -> Void) {
    if context.authenticator.aaid == "F1D0#1001" { // App PIN authenticator
        let credentialFormat = context.credentialFormat as! PINCredentialFormat
        // Present a screen to the user to allow them to enter their PIN. The transaction confirmation message is displayed on the same screen.
        showPinScreen(format: format, message: context.message, completion: handler)
    }
    else {
        // The user verification is handled by the SDK, the app only has to display the transaction confirmation message, if any. Otherwise, we can simply call the completion handler to continue the operation.
        if let message = context.message {
            showTransactionConfirmationMessage(message, completion: handler)
        }
        else {
            handler(.success(UserVerification.Outcome()))
        }
    }
}

Authenticator-specific Settings

The delegate is called by the SDK as part of the Open Settings operation to inform the delegate about available actions that can be performed using authenticator settings.

Diagram of Authenticator Settings

  1. The user opens settings in the application.
  2. The application calls the openSettings method from the NevisOperations protocol.
  3. The SDK calls the selectOpenSettingsOperation(context:completion:) delegate method from the OpenSettingsUserInteractionDelegate protocol. The AuthenticatorOperationSelection.Context parameter of the method contains a list of the available authenticators, and their supported operations, which could be used to inform the application what to select.
  4. The application sends back the desired authenticator and its operation to be performed.
  5. The SDK calls the openSettings(context:completion:) delegate method from the OpenSettingsUserInteractionDelegate protocol. The OpenSettings.Context parameter of the method contains information about the selected authenticator which could be used to set up a proper UI to perform the operation on the application side.
  6. The application implements a custom screen that shows an appropriate action to the user.
  7. The user performs the desired action, such as setting a new PIN or changing their PIN.
  8. The application sends the operation result to SDK, which finishes the operation.

Note
For the App PIN Authenticator a brute-force attack prevention is in place, and as a part of that, the Nevis Mobile Authentication SDK provides an AuthenticatorProtectionStatus object, containing information about current status of the PIN protection in PINAuthenticatorProtectionStatus. This is sent in the credential change scenario when using the Open Settings operation.

Example

func openSettings(_ context: AuthenticatorSettings.Context, completion handler: @escaping (Result<AuthenticatorSettings.Outcome>) -> Void) {
    // Present a settings screen to the user to handle enrollment or change of credentials, for example.
    showAuthenticatorSettings(with: context, completion: handler)
}