Error Handling

Overview

Methods defined in the public interface of the Nevis Mobile Authentication SDK are asynchronous and will always return their result using the Result type. This means that if something goes wrong while processing the requested operation, Result.failure(Error) will be returned as a result.

NEVIS Errors

The SDK always returns an error of type NevisError, which might have an underlying error that refers to the reason, showing what led to the problem.

The NevisError represents the list of errors that can happen in the NEVIS domain or in the FIDO layers, hence NEVIS errors contain a type (see NevisError.ErrorType) that indicates the type of error.

  • NEVIS domain: This layer is responsible for communicating with a NEVIS Mobile Authentication backend and interacts directly with the FIDO UAF Client in order to perform FIDO operations.
  • FIDO layers: i.e. authenticators, ASM or FIDO UAF Client.

In case the executed operation can be determined by the time the error occurred, then the operation property is filled with that. (See NevisOperationType.)

The list of possible result error types is documented on each method directly.

FIDO Errors

NOTE
FIDOError is an underlying error of a NevisError. In these cases the ErrorType of the NevisError is fidoError.

All errors that are raised in the FIDO layers are defined or mapped to FIDO errors, as defined in the specifications:

FIDO errors contain a code (see FIDOError.Code) that indicates the type of error as described in the UAF Error Code Interface.

Because authenticator errors are mapped to ASM errors, which are themselves mapped to FIDO Client errors, any error raised in those layers that is not part of the FIDO specifications will be mapped to a FIDO Client error (see FIDOError). Beside FIDOError.Code the FIDOError struct may contain the followings:

  • Such errors are represented by the FIDOError.Code.unknown code and may contain an optional underlying error that provides more information about the exact issue.
  • In case the authenticator can be determined by the time the error occurred then its aaid is propagated.

SDK Initialization Errors

NOTE
SdkInitializationError is an underlying error of a NevisError. In these cases the ErrorType of the NevisError is failedSdkInitialization.

Errors that can be thrown when the SDK is being initialized:

  • noDevicePasscodeError: This error is thrown if there is no device passcode configured.
  • deviceProtectionError: This error is thrown in case the SDK detects device protection violation. (Such violation can be for example if the device is rooted, or if debugger is attached to the application process).

Recoverable Errors

Errors are considered to be recoverable if a user intervention allows an ongoing erroneous operation to be completed successfully.

These types of errors occur because of miss-typed credentials or a failed biometric authentication which don’t require the whole operation to be restarted. For errors such as this, the SDK allows for user intervention (for example re-typing the credentials) whilst still taking into consideration brute force attack prevention.

In case a recoverable error occurs, the SDK calls again the UserInteractionDelegate.verifyUser delegate method extended with the reason of last failed attempts. This reason is stored in the UserVerification.Context.lastRecoverableError attribute as RecoverableError.

public enum RecoverableError {
    case badPinCredentials
}
///The context provided to the user interaction delegate for a user verification.
public struct Context {
    ...

    /// When a recoverable error occurred during the last verification of credentials, this method returns the reason of the last error.
    public let lastRecoverableError: RecoverableError?

    ...
}

⚠️️️ This behavior is valid for all non-system authenticators (currently for only PIN authenticator).

Examples

This section provides some example code snippets to handle errors originating from the Nevis Mobile Authentication SDK.

Given an In-band Registration operation, see code snippet below.

let userInteractionDelegate: UserInteractionDelegate = ...
session.register(username: username, dispatchTargetConfiguration: dispatchTargetConfiguration, authorizationProvider: authorizationProvider, userInteractionDelegate: userInteractionDelegate) { [unowned self] result in
    DispatchQueue.main.async {
        switch result {
        case .success:
            // Handle success case
        case let .failure(error):
            // Handle failure cases
            self.operation(.reg, didFailWith: error)
        }
    }
}

This can produce a variety of errors in case of failure. For a possible error handling, see code snippet below.

func operation(_ operation: NevisMobileAuthentication.Operation?, didFailWith error: Error) {
    if let nevisError = error as? NevisError {
        // Get the current operation that failed.
        // The SDK sends back the operation in `NevisError.operation`. Should that be nil, it is still possible to get a `NevisOperationType` from an `Operation`.
        let nevisOperation = nevisError.operation ?? operation?.toNevisOperationType
        if let fidoError = nevisError.lastUnderlyingFIDOError() {
            // Handle FIDO errors
            switch fidoError.code {
                ...
                case .authenticatorAccessDenied where nevisOperation == .registration:
                    // Handle authenticatorAccessDenied for registration operations
                ...
            }
        }
        else {
            // Handle non FIDO errors
            switch nevisError.type {
                ...
                case .networking where nevisOperation == .registration:
                    // Handle networking errors for registration operations
                ...
            }
        }
    }
    else {
        // Generic error handling
    }
}