Skip to content

Commit

Permalink
Merge pull request #559 from /issues/558-localized-biometric-error
Browse files Browse the repository at this point in the history
BiometricErrorInfo now contains localized reason of failure
  • Loading branch information
hvge authored Oct 3, 2023
2 parents b27e804 + 70d98e0 commit 6ab623b
Show file tree
Hide file tree
Showing 7 changed files with 293 additions and 107 deletions.
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

0 comments on commit 6ab623b

Please sign in to comment.