Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BiometricErrorInfo now contains localized reason of failure #559

Merged
merged 4 commits into from
Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 49 additions & 4 deletions docs/Migration-from-1.7-to-1.8.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,12 @@ PowerAuth Mobile SDK in version `1.8.0` provides the following improvements:

In case you need to still use the legacy setup to configure older version of PowerAuth mobile SDK, then you can use `get-legacy-config.swift` script available at `scripts` folder. For example:

```
./scripts/get-legacy-config.swift ARDTWDPw20CBb+aUeIuWy25MEHy89d2ySbQR2QoCb3taB1EBAUEEPspwnZzj7AOw0emEk/J51V16ZpkDMGE3VT3vzb+3Wh9qEA8MAJBTLPJ3XgFkr6OBVQCkpBezpbXOx1xHvVAqyQ==
```bash
# clone the mobile library
git clone https://github.com/wultra/powerauth-mobile-sdk.git
cd powerauth-mobile-sdk/scripts
# Show legacy config
./get-legacy-config.swift ARDTWDPw20CBb+aUeIuWy25MEHy89d2ySbQR2QoCb3taB1EBAUEEPspwnZzj7AOw0emEk/J51V16ZpkDMGE3VT3vzb+3Wh9qEA8MAJBTLPJ3XgFkr6OBVQCkpBezpbXOx1xHvVAqyQ==
Legacy PowerAuth configuration:
appKey : 01gz8NtAgW/mlHiLlstuTA==
appSecret : fLz13bJJtBHZCgJve1oHUQ==
Expand Down Expand Up @@ -59,12 +63,47 @@ Legacy PowerAuth configuration:
- `TIME_SYNCHRONIZATION` indicating a problem with the time synchronization.
- `BIOMETRY_NOT_ENROLLED` indicating that device has no enrolled biometry.

- The biometry-related methods in `PowerAuthSDK` are no longer annotated as `@RequiresApi(api = Build.VERSION_CODES.M)`. This change may lead to a several dead code branches in your code if you still support devices older than Android 6.0.
- The biometry-related methods in `PowerAuthSDK` are no longer annotated as `@RequiresApi(api = Build.VERSION_CODES.M)`. This change may lead to a several dead code branches in your code if you still support devices older than Android 6.0.

- Removed all interfaces deprecated in release `1.7.x`

### Other changes

#### Biometric Authentication

If the `PowerAuthErrorException` is related to a biometric authentication failure, then the new `additionalInformation` property will contain an instance of the `BiometricErrorInfo` class. It's recommended to test whether the reason for the failure was presented to the user in the authentication dialog or in a custom error dialog provided by the PowerAuth mobile SDK. For example:

```kotlin
// Authenticate user with biometry and obtain encrypted biometry factor related key.
powerAuthSDK.authenticateUsingBiometrics(context, fragment, "Sign in", "Use the biometric sensor on your device to continue", object: IAuthenticateWithBiometricsListener {
override fun onBiometricDialogCancelled(userCancel: Boolean) {
// User or system cancelled the operation
}

override fun onBiometricDialogSuccess(authentication: PowerAuthAuthentication) {
// Success
}

override fun onBiometricDialogFailed(error: PowerAuthErrorException) {
val biometricErrorInfo = error.additionalInformation as? BiometricErrorInfo
if (biometricErrorInfo != null) {
if (biometricErrorInfo.isErrorPresentationRequired) {
// The application should present the reason for the biometric authentication failure to the user.
//
// If you don't disable the error dialog provided by the PowerAuth mobile SDK, then this may happen
// only when you try to use the biometric authentication while the biometric factor is not configured
// in the PowerAuthSDK instance.
val localizedMessage = biometricErrorInfo.getLocalizedErrorMessage(context, null)
}
} else {
// Other reason for failure
}
}
})
```

See also [Disable Error Dialog After Failed Biometry](PowerAuth-SDK-for-Android.md#disable-error-dialog-after-failed-biometry) chapter for more details.

#### Synchronized time

The requirement for the time synchronized with the server has the following impact to your code:
Expand Down Expand Up @@ -252,4 +291,10 @@ Visit [Synchronized Time](https://developers.wultra.com/components/powerauth-mob

## Known Bugs

The PowerAuth SDKs for iOS and tvOS App Extensions, as well as for watchOS, do not use time synchronized with the server for token-based authentication. To avoid any compatibility issues with the server, the authentication headers generated in your App Extension or on watchOS still use the older protocol version 3.1. This issue will be fixed in a future SDK update.
The PowerAuth SDKs for iOS and tvOS App Extensions, as well as for watchOS, do not use time synchronized with the server for token-based authentication. To avoid any compatibility issues with the server, the authentication headers generated in your App Extension or on watchOS still use the older protocol version 3.1. This issue will be fixed in a future SDK update.

You can watch the following related issues:

- [wultra/powerauth-mobile-sdk#551](https://github.com/wultra/powerauth-mobile-sdk/issues/551)
- [wultra/powerauth-mobile-watch-sdk#7](https://github.com/wultra/powerauth-mobile-watch-sdk/issues/7)
- [wultra/powerauth-mobile-extensions-sdk#7](https://github.com/wultra/powerauth-mobile-extensions-sdk/issues/7)
70 changes: 53 additions & 17 deletions docs/PowerAuth-SDK-for-Android.md
Original file line number Diff line number Diff line change
Expand Up @@ -1783,6 +1783,19 @@ powerAuthSDK.authenticateUsingBiometrics(context, fragment, "Sign in", "Use the

override fun onBiometricDialogFailed(error: PowerAuthErrorException) {
// Biometric authentication failed
val biometricErrorInfo = error.additionalInformation as? BiometricErrorInfo
if (biometricErrorInfo != null) {
if (biometricErrorInfo.isErrorPresentationRequired) {
// The application should present the reason for the biometric authentication failure to the user.
//
// If you don't disable the error dialog provided by the PowerAuth mobile SDK, then this may happen
// only when you try to use the biometric authentication while the biometric factor is not configured
// in the PowerAuthSDK instance.
val localizedMessage = biometricErrorInfo.getLocalizedErrorMessage(context, null)
}
} else {
// Other reason for failure
}
}
})
```
Expand All @@ -1802,6 +1815,19 @@ powerAuthSDK.authenticateUsingBiometrics(context, fragment, "Sign in", "Use the
@Override
public void onBiometricDialogFailed(PowerAuthErrorException error) {
// Biometric authentication failed
if (error.getAdditionalInfo() instanceof BiometricErrorInfo) {
BiometricErrorInfo biometricErrorInfo = (BiometricErrorInfo) error.getAdditionalInfo();
if (biometricErrorInfo.isErrorPresentationRequired()) {
// The application should present the reason for the biometric authentication failure to the user.
//
// If you don't disable the error dialog provided by the PowerAuth mobile SDK, then this may happen
// only when you try to use the biometric authentication while the biometric factor is not configured
// in the PowerAuthSDK instance.
String localizedMessage = biometricErrorInfo.getLocalizedErrorMessage(context, null);
}
} else {
// Other reason for failure
}
}
});
```
Expand Down Expand Up @@ -1895,7 +1921,7 @@ If you prefer not to allow the PowerAuth mobile SDK to display its own error dia
BiometricAuthentication.setBiometricErrorDialogDisabled(true)
```

When the error dialog is disabled, your application should inform the user of the reason for the failure. Handling this might be somewhat tricky because there are situations where the biometric authentication dialog is not displayed at all, and the failure is reported directly to the application. To address this, you can use the `BiometricErrorInfo` enumeration, which is associated with the reported `PowerAuthErrorException`. The code snippet below outlines how to determine the situation:
When the error dialog is disabled, your application should inform the user of the reason for the failure. Handling this might be somewhat tricky because there are situations where the biometric authentication dialog is not displayed at all, and the failure is reported directly to the application. To address this, you can use the `BiometricErrorInfo` class, which is associated with the reported `PowerAuthErrorException`. The code snippet below outlines how to determine the situation:

```kotlin
// Authenticate user with biometry and obtain encrypted biometry factor related key.
Expand All @@ -1909,20 +1935,23 @@ powerAuthSDK.authenticateUsingBiometrics(context, fragment, "Sign in", "Use the
}

override fun onBiometricDialogFailed(error: PowerAuthErrorException) {
if (error.additionalInformation == BiometricErrorInfo.BIOMETRICS_FAILED_WITH_NO_VISIBLE_REASON) {
// Application should display error in its own UI
when (error.powerAuthErrorCode) {
PowerAuthErrorCodes.BIOMETRY_LOCKOUT -> println("Lockout")
PowerAuthErrorCodes.BIOMETRY_NOT_AVAILABLE -> println("Not available, try later")
PowerAuthErrorCodes.BIOMETRY_NOT_RECOGNIZED -> println("Fingerprint or face not recognized") // check inline documentation for more details
PowerAuthErrorCodes.BIOMETRY_NOT_SUPPORTED -> println("Device has no biometry sensor")
PowerAuthErrorCodes.BIOMETRY_NOT_ENROLLED -> println("Device has no biometry data enrolled")
val biometricErrorInfo = error.additionalInformation as? BiometricErrorInfo
if (biometricErrorInfo != null) {
if (biometricErrorInfo.isErrorPresentationRequired) {
// Application should present reason of biometric authentication failure to the user
val localizedMessage = biometricErrorInfo.getLocalizedErrorMessage(context, null)
}
} else {
// Other reason for failure
}
}
})
```

<!-- begin box info -->
Note that you still should [Customize Biometric Dialog Resources](#customize-biometric-dialog-resources) to get a proper localized error message.
<!-- end -->

#### Biometric Authentication Confirmation

On Android 10+ systems, it's possible to configure `BiometricPrompt` to ask for an additional confirmation after the user is successfully authenticated. The default behavior for PowerAuth Mobile SDK is that such confirmation is not required. To change this behavior, you have to provide `PowerAuthKeychainConfiguration` object with `confirmBiometricAuthentication` parameter set to `true` and use that configuration for the `PowerAuthSDK` instance construction:
Expand Down Expand Up @@ -2604,7 +2633,7 @@ when (t) {
PowerAuthErrorCodes.BIOMETRY_CANCEL -> Log.d(TAG, "Error code for Biometry action cancel error")
PowerAuthErrorCodes.BIOMETRY_NOT_SUPPORTED -> Log.d(TAG, "The device or operating system doesn't support biometric authentication.")
PowerAuthErrorCodes.BIOMETRY_NOT_AVAILABLE -> Log.d(TAG, "The biometric authentication is temporarily unavailable.")
PowerAuthErrorCodes.BIOMETRY_NOT_RECOGNIZED -> Log.d(TAG, "The biometric authentication did not recognize the biometric image (fingerprint, face, etc...)")
PowerAuthErrorCodes.BIOMETRY_NOT_RECOGNIZED -> Log.d(TAG, "The biometric authentication did not recognize the biometric image") // Reported only during biometric authentication setup
PowerAuthErrorCodes.BIOMETRY_NOT_ENROLLED -> Log.d(TAG, "The biometric authentication failed because there's no biometry enrolled")
PowerAuthErrorCodes.BIOMETRY_LOCKOUT -> Log.d(TAG, "The biometric authentication is locked out due to too many failed attempts.")
PowerAuthErrorCodes.OPERATION_CANCELED -> Log.d(TAG, "Error code for cancelled operations")
Expand All @@ -2615,10 +2644,13 @@ when (t) {
PowerAuthErrorCodes.TIME_SYNCHRONIZATION -> Log.d(TAG, "Failed to synchronize time with the server.")
}
// Process additional information
when (t.additionalInformation) {
BiometricErrorInfo.BIOMETRICS_FAILED_WITH_NO_VISIBLE_REASON -> {
// Application should display error dialog after failed biometric authentication. This is relevant only
// if you disabled the biometric error dialog provided by PowerAuth mobile SDK.
val additionalInfo = error.additionalInformation
when (additionalInfo) {
is BiometricErrorInfo -> {
if (additionalInfo.isErrorPresentationRequired) {
// Application should display error dialog after failed biometric authentication. This is relevant only
// if you disabled the biometric error dialog provided by PowerAuth mobile SDK.
}
}
}
}
Expand Down Expand Up @@ -2662,6 +2694,7 @@ if (t instanceof PowerAuthErrorException) {
case PowerAuthErrorCodes.BIOMETRY_NOT_AVAILABLE:
android.util.Log.d(TAG,"The biometric authentication is temporarily unavailable."); break;
case PowerAuthErrorCodes.BIOMETRY_NOT_RECOGNIZED:
// Reported only during biometric authentication setup
android.util.Log.d(TAG,"The biometric authentication did not recognize the biometric image (fingerprint, face, etc...)"); break;
case PowerAuthErrorCodes.BIOMETRY_NOT_ENROLLED:
android.util.Log.d(TAG,"The biometric authentication failed because there's no biometry enrolled"); break;
Expand All @@ -2681,9 +2714,12 @@ if (t instanceof PowerAuthErrorException) {
android.util.Log.d(TAG,"Failed to synchronize time with the server."); break;
}
// Process additional information
if (BiometricErrorInfo.BIOMETRICS_FAILED_WITH_NO_VISIBLE_REASON.equals(exception.getAdditionalInformation())) {
// Application should display error dialog after failed biometric authentication. This is relevant only
// if you disabled the biometric error dialog provided by PowerAuth mobile SDK.
if (exception.getAdditionalInformation() instanceof BiometricErrorInfo) {
BiometricErrorInfo biometricErrorInfo = (BiometricErrorInfo) exception.getAdditionalInformation();
if (biometricErrorInfo.isErrorPresentationRequired()) {
// Application should display error dialog after failed biometric authentication. This is relevant only
// if you disabled the biometric error dialog provided by PowerAuth mobile SDK.
}
}

} else if (t instanceof ErrorResponseApiException) {
Expand Down
Loading