diff --git a/.gitignore b/.gitignore index 38567756..af2dc726 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,9 @@ xcuserdata *.xcuserstate *.xcscmblueprint +## VSCode +.vscode + ## Obj-C/Swift specific *.hmap *.ipa diff --git a/docs/PowerAuth-SDK-for-Android.md b/docs/PowerAuth-SDK-for-Android.md index 8ff919f3..b4e36947 100644 --- a/docs/PowerAuth-SDK-for-Android.md +++ b/docs/PowerAuth-SDK-for-Android.md @@ -597,7 +597,7 @@ This code has created activation with two factors: possession (key stored using ```kotlin // Persist activation using given PIN and ad-hoc generated biometric related key -powerAuthSDK.persistActivation(context, fragment, "Enable Biometric Authentication", "To enable biometric authentication, use the biometric sensor on your device.", pin, object: IPersistActivationWithBiometryListener { +powerAuthSDK.persistActivation(context, fragment, "Enable Biometric Authentication", "To enable biometric authentication, use the biometric sensor on your device.", pin, object: IPersistActivationWithBiometricsListener { override fun onBiometricDialogCancelled() { // Biometric enrolment cancelled by user } @@ -613,7 +613,7 @@ powerAuthSDK.persistActivation(context, fragment, "Enable Biometric Authenticati ``` ```java // Persist activation using given PIN and ad-hoc generated biometric related key -powerAuthSDK.persistActivation(context, fragment, "Enable Biometric Authentication", "To enable biometric authentication, use the biometric sensor on your device.", pin, new IPersistActivationWithBiometryListener() { +powerAuthSDK.persistActivation(context, fragment, "Enable Biometric Authentication", "To enable biometric authentication, use the biometric sensor on your device.", pin, new IPersistActivationWithBiometricsListener() { @Override public void onBiometricDialogCancelled() { // Biometric enrolment cancelled by user @@ -1772,15 +1772,13 @@ In order to obtain an encrypted biometry factor-related key for the purpose of a ```kotlin // Authenticate user with biometry and obtain encrypted biometry factor related key. -powerAuthSDK.authenticateUsingBiometry(context, fragment, "Sign in", "Use the biometric sensor on your device to continue", object: IBiometricAuthenticationCallback { +powerAuthSDK.authenticateUsingBiometrics(context, fragment, "Sign in", "Use the biometric sensor on your device to continue", object: IAuthenticateWithBiometricsListener { override fun onBiometricDialogCancelled(userCancel: Boolean) { // User cancelled the operation } - override fun onBiometricDialogSuccess(biometricKeyData: BiometricKeyData) { - // User authenticated and biometry key was returned, now you can construct PowerAuthAuthentication object with proper signing capabilities. - val biometryFactorRelatedKey = biometricKeyData.derivedData - val twoFactorBiometry = PowerAuthAuthentication.possessionWithBiometry(biometryFactorRelatedKey) + override fun onBiometricDialogSuccess(authentication: PowerAuthAuthentication) { + // User authenticated use the provided authentication object for other tasks. } override fun onBiometricDialogFailed(error: PowerAuthErrorException) { @@ -1790,17 +1788,15 @@ powerAuthSDK.authenticateUsingBiometry(context, fragment, "Sign in", "Use the bi ``` ```java // Authenticate user with biometry and obtain encrypted biometry factor related key. -powerAuthSDK.authenticateUsingBiometry(context, fragment, "Sign in", "Use the biometric sensor on your device to continue", new IBiometricAuthenticationCallback() { +powerAuthSDK.authenticateUsingBiometrics(context, fragment, "Sign in", "Use the biometric sensor on your device to continue", new IAuthenticateWithBiometricsListener() { @Override public void onBiometricDialogCancelled(boolean userCancel) { // User cancelled the operation } @Override - public void onBiometricDialogSuccess(BiometricKeyData biometricKeyData) { - // User authenticated and biometry key was returned, now you can construct PowerAuthAuthentication object with proper signing capabilities. - final byte[] biometryFactorRelatedKey = biometricKeyData.getDerivedData(); - final PowerAuthAuthentication twoFactorBiometry = PowerAuthAuthentication.possessionWithBiometry(biometryFactorRelatedKey); + public void onBiometricDialogSuccess(PowerAuthAuthentication authentication) { + // User authenticated use the provided authentication object for other tasks. } @Override @@ -1848,6 +1844,8 @@ Be aware that the configuration above is effective only for the new keys. So, if The `BiometricAuthentication` class is a high level interface that provides interfaces related to the biometric authentication for the SDK, or for the application purposes. The class hides all technical details, so it can be safely used also on the systems that doesn't provide biometric interfaces, or if the system has no biometric sensor available. The implementation under the hood uses `androidx.biometric.BiometricPrompt` and `androidx.biometric.BiometricManager` classes. +#### Customize Biometric Dialog Resources + To customize the strings used in biometric authentication, you can use `BiometricDialogResources` in the following manner: @@ -1879,6 +1877,44 @@ BiometricAuthentication.setBiometricDialogResources(resources); ``` +#### Disable Error Dialog After Failed Biometry + +If you prefer not to allow the PowerAuth mobile SDK to display its own error dialog, you can disable this feature globally. In this case, your application will need to handle all error situations through its own user interface. Use the following code to disable the error dialog: + +```kotlin +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: + +```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) { + 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") + } + } + } +}) +``` + +#### 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: @@ -2559,6 +2595,7 @@ when (t) { 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_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") PowerAuthErrorCodes.ENCRYPTION_ERROR -> Log.d(TAG, "Error code for errors related to end-to-end encryption") @@ -2567,6 +2604,13 @@ when (t) { PowerAuthErrorCodes.PENDING_PROTOCOL_UPGRADE -> Log.d(TAG, "The operation is temporarily unavailable, due to pending protocol upgrade.") 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. + } + } } is ErrorResponseApiException -> { val errorResponse: Error? = t.errorResponse @@ -2609,6 +2653,8 @@ if (t instanceof PowerAuthErrorException) { android.util.Log.d(TAG,"The biometric authentication is temporarily unavailable."); break; case PowerAuthErrorCodes.BIOMETRY_NOT_RECOGNIZED: 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; case PowerAuthErrorCodes.BIOMETRY_LOCKOUT: android.util.Log.d(TAG,"The biometric authentication is locked out due to too many failed attempts."); break; case PowerAuthErrorCodes.OPERATION_CANCELED: @@ -2624,6 +2670,12 @@ if (t instanceof PowerAuthErrorException) { case PowerAuthErrorCodes.TIME_SYNCHRONIZATION: 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. + } + } else if (t instanceof ErrorResponseApiException) { ErrorResponseApiException exception = (ErrorResponseApiException) t; Error errorResponse = exception.getErrorResponse(); diff --git a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/BiometricAuthentication.java b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/BiometricAuthentication.java index f49bd9ad..979ec659 100644 --- a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/BiometricAuthentication.java +++ b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/BiometricAuthentication.java @@ -139,7 +139,7 @@ public void onBiometricKeyUnavailable() { } }); final IBiometricKeyEncryptorProvider biometricKeyEncryptorProvider = new DefaultBiometricKeyEncryptorProvider(request, getBiometricKeystore()); - final PrivateRequestData requestData = new PrivateRequestData(request, biometricKeyEncryptorProvider, dispatcher, ctx.getBiometricDialogResources()); + final PrivateRequestData requestData = new PrivateRequestData(request, biometricKeyEncryptorProvider, dispatcher, ctx.getBiometricDialogResources(), ctx.isBiometricErrorDialogDisabled()); // Validate request status @BiometricStatus int status = device.canAuthenticate(); @@ -176,7 +176,14 @@ public void onBiometricKeyUnavailable() { if (exception == null) { exception = BiometricHelper.getExceptionForBiometricStatus(status); } - return showErrorDialog(status, exception, context, requestData); + if (requestData.isErrorDialogDisabled()) { + // Error dialog is disabled, so report the error immediately. Use "no visible reason" hint. + dispatcher.dispatchError(BiometricErrorInfo.BIOMETRICS_FAILED_WITH_NO_VISIBLE_REASON.addToException(exception)); + return dispatcher.getCancelableTask(); + } else { + // Error dialog is not disabled, so we can show it. Use "visible reason" hint. + return showErrorDialog(status, BiometricErrorInfo.BIOMETRICS_FAILED_WITH_VISIBLE_REASON.addToException(exception), context, requestData); + } } } @@ -310,6 +317,32 @@ public static void setBiometricDialogResources(@NonNull BiometricDialogResources } } + /** + * Disable or enable error dialog provided by PowerAuth mobile SDK and displayed after failed biometric authentication. + *
+ * If set to {@code true}, then the custom error dialog provided by the PowerAuth mobile SDK will never + * be displayed in the case of authentication failure. The mobile application should handle all possible error + * states using its own UI elements. The default value for this property is {@code false}, and the PowerAuth mobile + * SDK may display its own error dialog. + * + * @param disabled If {@code true}, then the PowerAuth mobile SDK will never display its own error dialog. + */ + public static void setBiometricErrorDialogDisabled(boolean disabled) { + synchronized (SharedContext.class) { + getContext().setBiometricErrorDialogDisabled(disabled); + } + } + + /** + * Return information whether error dialog provided by PowerAuth mobile SDK is disabled or enabled. + * @return {@code true} in case that the PowerAuth mobile SDK will never display its own error dialog, {@code false} otherwise. + */ + public static boolean isBiometricErrorDialogDisabled() { + synchronized (SharedContext.class) { + return getContext().isBiometricErrorDialogDisabled(); + } + } + /** * Return type of biometry supported on the system. * @@ -344,6 +377,12 @@ private static class SharedContext { */ private @Nullable IBiometricAuthenticator authenticator; + /** + * Contains {@code true} in case that application want's to deal with authentication errors in its own UI. + * The default value is {@code false}; + */ + private boolean isBiometricErrorDialogDisabled = false; + /** * Contains {@code true} in case that there's already pending biometric authentication. */ @@ -372,6 +411,20 @@ void setBiometricDialogResources(@NonNull BiometricDialogResources resources) { return biometricDialogResources; } + /** + * @param disabled if true, then error dialog provided by PowerAuth mobile SDK will be disabled. + */ + void setBiometricErrorDialogDisabled(boolean disabled) { + isBiometricErrorDialogDisabled = disabled; + } + + /** + * @return true when error dialog provided by PowerAuth mobile SDK is be disabled. + */ + boolean isBiometricErrorDialogDisabled() { + return isBiometricErrorDialogDisabled; + } + /** * Returns object implementing {@link IBiometricAuthenticator} interface. The returned implementation * depends on the version of Android system and on the authenticator's capabilities. If current system diff --git a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/BiometricErrorInfo.java b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/BiometricErrorInfo.java new file mode 100644 index 00000000..4f544d01 --- /dev/null +++ b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/BiometricErrorInfo.java @@ -0,0 +1,66 @@ +/* + * Copyright 2023 Wultra s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.getlime.security.powerauth.biometry; + +import androidx.annotation.NonNull; +import io.getlime.security.powerauth.exception.PowerAuthErrorCodes; +import io.getlime.security.powerauth.exception.PowerAuthErrorException; + +/** + * The {@code BiometricErrorInfo} enumeration contains an information associated with {@link PowerAuthErrorException}. + * The enumeration is available only if the exception's error code is one of: + *
+ * See issue: wultra/powerauth-mobile-sdk#422
* @return true if quick cancel after failure is detected.
*/
private boolean detectQuickCancelAfterFailure() {
diff --git a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/impl/PrivateRequestData.java b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/impl/PrivateRequestData.java
index 273c4fd5..6f58a173 100644
--- a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/impl/PrivateRequestData.java
+++ b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/impl/PrivateRequestData.java
@@ -34,6 +34,7 @@ public class PrivateRequestData {
private final @NonNull BiometricResultDispatcher dispatcher;
private final @NonNull BiometricDialogResources resources;
private final @NonNull IBiometricKeyEncryptorProvider biometricKeyEncryptorProvider;
+ private final boolean errorDialogDisabled;
private final long creationTime;
/**
@@ -43,16 +44,19 @@ public class PrivateRequestData {
* @param biometricKeyEncryptorProvider Object that provide {@link IBiometricKeyEncryptor} on demand.
* @param dispatcher Dispatcher that holds completion callback and callback dispatcher.
* @param resources Resources required for the legacy implementation.
+ * @param errorDialogDisabled If true then error dialog should not be displayed.
*/
public PrivateRequestData(@NonNull BiometricAuthenticationRequest request,
@NonNull IBiometricKeyEncryptorProvider biometricKeyEncryptorProvider,
@NonNull BiometricResultDispatcher dispatcher,
- @NonNull BiometricDialogResources resources) {
+ @NonNull BiometricDialogResources resources,
+ boolean errorDialogDisabled) {
this.request = request;
this.biometricKeyEncryptorProvider = biometricKeyEncryptorProvider;
this.dispatcher = dispatcher;
this.resources = resources;
this.creationTime = SystemClock.elapsedRealtime();
+ this.errorDialogDisabled = errorDialogDisabled;
}
/**
@@ -90,6 +94,13 @@ public long getElapsedTime() {
return SystemClock.elapsedRealtime() - creationTime;
}
+ /**
+ * @return {@code true} if error dialog after failed authentication should not be displayed.
+ */
+ public boolean isErrorDialogDisabled() {
+ return errorDialogDisabled;
+ }
+
/**
* This helper method return {@link FragmentManager} from Fragment or FragmentActivity
* provided in {@link BiometricAuthenticationRequest}. The method throws {@code IllegalStateException}
diff --git a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/exception/PowerAuthErrorCodes.java b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/exception/PowerAuthErrorCodes.java
index 31eb1e22..a34d28ec 100644
--- a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/exception/PowerAuthErrorCodes.java
+++ b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/exception/PowerAuthErrorCodes.java
@@ -33,7 +33,8 @@
INVALID_TOKEN, ENCRYPTION_ERROR, WRONG_PARAMETER,
PROTOCOL_UPGRADE, PENDING_PROTOCOL_UPGRADE,
BIOMETRY_NOT_SUPPORTED, BIOMETRY_NOT_AVAILABLE, BIOMETRY_NOT_RECOGNIZED,
- INSUFFICIENT_KEYCHAIN_PROTECTION, BIOMETRY_LOCKOUT, TIME_SYNCHRONIZATION})
+ INSUFFICIENT_KEYCHAIN_PROTECTION, BIOMETRY_LOCKOUT, TIME_SYNCHRONIZATION,
+ BIOMETRY_NOT_ENROLLED})
public @interface PowerAuthErrorCodes {
/**
@@ -162,4 +163,9 @@
* Failed to synchronize time with the server.
*/
int TIME_SYNCHRONIZATION = 23;
+
+ /**
+ * The biometric authentication failed because there's no biometry enrolled on the device.
+ */
+ int BIOMETRY_NOT_ENROLLED = 24;
}
diff --git a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/exception/PowerAuthErrorException.java b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/exception/PowerAuthErrorException.java
index 378d8d59..5d68f088 100644
--- a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/exception/PowerAuthErrorException.java
+++ b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/exception/PowerAuthErrorException.java
@@ -17,6 +17,7 @@
package io.getlime.security.powerauth.exception;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
/**
* Will be thrown, or will be returned to listener, in case that requested operation fails
@@ -29,12 +30,17 @@ public class PowerAuthErrorException extends Exception {
*/
@PowerAuthErrorCodes
private final int powerAuthErrorCode;
+ /**
+ * Additional information associated with the failure reason. The m
+ */
+ private final Object additionalInformation;
/**
* @param powerAuthErrorCode Integer constant from {@link PowerAuthErrorCodes}
*/
public PowerAuthErrorException(@PowerAuthErrorCodes int powerAuthErrorCode) {
this.powerAuthErrorCode = powerAuthErrorCode;
+ this.additionalInformation = null;
}
/**
@@ -44,6 +50,7 @@ public PowerAuthErrorException(@PowerAuthErrorCodes int powerAuthErrorCode) {
public PowerAuthErrorException(@PowerAuthErrorCodes int powerAuthErrorCode, String message) {
super(message);
this.powerAuthErrorCode = powerAuthErrorCode;
+ this.additionalInformation = null;
}
/**
@@ -54,6 +61,19 @@ public PowerAuthErrorException(@PowerAuthErrorCodes int powerAuthErrorCode, Stri
public PowerAuthErrorException(@PowerAuthErrorCodes int powerAuthErrorCode, String message, Throwable cause) {
super(message, cause);
this.powerAuthErrorCode = powerAuthErrorCode;
+ this.additionalInformation = null;
+ }
+
+ /**
+ * @param powerAuthErrorCode Integer constant from {@link PowerAuthErrorCodes}
+ * @param message String with detailed error description.
+ * @param cause Original cause of failure.
+ * @param additionalInformation Additional information.
+ */
+ public PowerAuthErrorException(@PowerAuthErrorCodes int powerAuthErrorCode, String message, Throwable cause, Object additionalInformation) {
+ super(message, cause);
+ this.powerAuthErrorCode = powerAuthErrorCode;
+ this.additionalInformation = additionalInformation;
}
/**
@@ -64,6 +84,16 @@ public int getPowerAuthErrorCode() {
return powerAuthErrorCode;
}
+ /**
+ * Get additional information that may help with the error processing. If the error is biometry-related, then
+ * you can obtain {@link io.getlime.security.powerauth.biometry.BiometricErrorInfo} enumeration in this property.
+ * @return Additional information that help with error processing.
+ */
+ @Nullable
+ public Object getAdditionalInformation() {
+ return additionalInformation;
+ }
+
/**
* Wrap {@link Throwable} cause of failure into {@link PowerAuthErrorException} with provided
* error code and message. In case that original exception is already {@link PowerAuthErrorException},
diff --git a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/networking/response/IGenerateTokenHeaderListener.java b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/networking/response/IGenerateTokenHeaderListener.java
index 6ec24b8c..ed513a88 100644
--- a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/networking/response/IGenerateTokenHeaderListener.java
+++ b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/networking/response/IGenerateTokenHeaderListener.java
@@ -26,6 +26,8 @@
public interface IGenerateTokenHeaderListener {
/**
* Called when generating token header succeeded.
+ *
+ * @param header Authorization header.
*/
@MainThread
void onGenerateTokenHeaderSucceeded(@NonNull PowerAuthAuthorizationHttpHeader header);
diff --git a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/networking/response/IServerStatusListener.java b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/networking/response/IServerStatusListener.java
index d9e8db2b..13e52b49 100644
--- a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/networking/response/IServerStatusListener.java
+++ b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/networking/response/IServerStatusListener.java
@@ -25,6 +25,8 @@
public interface IServerStatusListener {
/**
* Called when getting server status succeeded.
+ *
+ * @param status Received server status.
*/
@MainThread
void onServerStatusSucceeded(@NonNull ServerStatus status);
diff --git a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthConfiguration.java b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthConfiguration.java
index e3bac289..0c9b4f1a 100644
--- a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthConfiguration.java
+++ b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthConfiguration.java
@@ -16,15 +16,12 @@
package io.getlime.security.powerauth.sdk;
-import android.content.Context;
-
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.Arrays;
import io.getlime.security.powerauth.core.SessionSetup;
-import io.getlime.security.powerauth.sdk.impl.IPossessionFactorEncryptionKeyProvider;
/**
* Class representing a configuration of a single PowerAuthSDK instance.
diff --git a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthSDK.java b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthSDK.java
index 44ee4a6e..38f2f54a 100644
--- a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthSDK.java
+++ b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthSDK.java
@@ -940,8 +940,8 @@ public ICancelable persistActivation(
@NonNull String title,
@NonNull String description,
@NonNull final String password,
- final @NonNull IPersistActivationWithBiometryListener callback) {
- return persistActivationWithBiometryImpl(context, FragmentHelper.from(fragmentActivity), title, description, new Password(password), callback);
+ final @NonNull IPersistActivationWithBiometricsListener callback) {
+ return persistActivationWithBiometricsImpl(context, FragmentHelper.from(fragmentActivity), title, description, new Password(password), callback);
}
/**
@@ -963,8 +963,8 @@ public ICancelable persistActivation(
@NonNull String title,
@NonNull String description,
@NonNull final Password password,
- final @NonNull IPersistActivationWithBiometryListener callback) {
- return persistActivationWithBiometryImpl(context, FragmentHelper.from(fragmentActivity), title, description, password, callback);
+ final @NonNull IPersistActivationWithBiometricsListener callback) {
+ return persistActivationWithBiometricsImpl(context, FragmentHelper.from(fragmentActivity), title, description, password, callback);
}
/**
@@ -986,8 +986,8 @@ public ICancelable persistActivation(
@NonNull String title,
@NonNull String description,
@NonNull final String password,
- final @NonNull IPersistActivationWithBiometryListener callback) {
- return persistActivationWithBiometryImpl(context, FragmentHelper.from(fragment), title, description, new Password(password), callback);
+ final @NonNull IPersistActivationWithBiometricsListener callback) {
+ return persistActivationWithBiometricsImpl(context, FragmentHelper.from(fragment), title, description, new Password(password), callback);
}
/**
@@ -1009,8 +1009,8 @@ public ICancelable persistActivation(
@NonNull String title,
@NonNull String description,
@NonNull final Password password,
- final @NonNull IPersistActivationWithBiometryListener callback) {
- return persistActivationWithBiometryImpl(context, FragmentHelper.from(fragment), title, description, password, callback);
+ final @NonNull IPersistActivationWithBiometricsListener callback) {
+ return persistActivationWithBiometricsImpl(context, FragmentHelper.from(fragment), title, description, password, callback);
}
/**
@@ -1026,14 +1026,14 @@ public ICancelable persistActivation(
*/
@UiThread
@NonNull
- private ICancelable persistActivationWithBiometryImpl(
+ private ICancelable persistActivationWithBiometricsImpl(
final @NonNull Context context,
@NonNull FragmentHelper fragmentHelper,
@NonNull String title,
@NonNull String description,
@NonNull final Password password,
- final @NonNull IPersistActivationWithBiometryListener callback) {
- return authenticateUsingBiometry(context, fragmentHelper, title, description, true, new IBiometricAuthenticationCallback() {
+ final @NonNull IPersistActivationWithBiometricsListener callback) {
+ return authenticateUsingBiometrics(context, fragmentHelper, title, description, true, new IBiometricAuthenticationCallback() {
@Override
public void onBiometricDialogCancelled(boolean userCancel) {
if (userCancel) {
@@ -1237,7 +1237,7 @@ public int commitActivationWithPassword(@NonNull Context context, @NonNull Strin
* @param password Password used to persist activation.
* @param callback Callback with the authentication result.
* @return {@link ICancelable} object associated with the biometric prompt.
- * @deprecated Use {@link #persistActivation(Context, Fragment, String, String, Password, IPersistActivationWithBiometryListener)} as a replacement.
+ * @deprecated Use {@link #persistActivation(Context, Fragment, String, String, Password, IPersistActivationWithBiometricsListener)} as a replacement.
*/
@UiThread
@NonNull
@@ -1248,7 +1248,7 @@ public ICancelable commitActivation(
@NonNull String title,
@NonNull String description,
@NonNull final Password password,
- final @NonNull IPersistActivationWithBiometryListener callback) {
+ final @NonNull IPersistActivationWithBiometricsListener callback) {
return persistActivation(context, fragment, title, description, password, callback);
}
@@ -1262,7 +1262,7 @@ public ICancelable commitActivation(
* @param password Password used to persist activation.
* @param callback Callback with the authentication result.
* @return {@link ICancelable} object associated with the biometric prompt.
- * @deprecated Use {@link #persistActivation(Context, Fragment, String, String, String, IPersistActivationWithBiometryListener)} as a replacement.
+ * @deprecated Use {@link #persistActivation(Context, Fragment, String, String, String, IPersistActivationWithBiometricsListener)} as a replacement.
*/
@UiThread
@NonNull
@@ -1273,7 +1273,7 @@ public ICancelable commitActivation(
@NonNull String title,
@NonNull String description,
@NonNull final String password,
- final @NonNull IPersistActivationWithBiometryListener callback) {
+ final @NonNull IPersistActivationWithBiometricsListener callback) {
return persistActivation(context, fragment, title, description, password, callback);
}
@@ -1287,7 +1287,7 @@ public ICancelable commitActivation(
* @param password Password used to persist activation.
* @param callback Callback with the authentication result.
* @return {@link ICancelable} object associated with the biometric prompt.
- * @deprecated Use {@link #persistActivation(Context, FragmentActivity, String, String, String, IPersistActivationWithBiometryListener)} as a replacement.
+ * @deprecated Use {@link #persistActivation(Context, FragmentActivity, String, String, String, IPersistActivationWithBiometricsListener)} as a replacement.
*/
@UiThread
@NonNull
@@ -1298,7 +1298,7 @@ public ICancelable commitActivation(
@NonNull String title,
@NonNull String description,
@NonNull final String password,
- final @NonNull IPersistActivationWithBiometryListener callback) {
+ final @NonNull IPersistActivationWithBiometricsListener callback) {
return persistActivation(context, fragmentActivity, title, description, password, callback);
}
@@ -1312,7 +1312,7 @@ public ICancelable commitActivation(
* @param password Password used to persist activation.
* @param callback Callback with the authentication result.
* @return {@link ICancelable} object associated with the biometric prompt.
- * @deprecated Use {@link #persistActivation(Context, FragmentActivity, String, String, Password, IPersistActivationWithBiometryListener)} as a replacement.
+ * @deprecated Use {@link #persistActivation(Context, FragmentActivity, String, String, Password, IPersistActivationWithBiometricsListener)} as a replacement.
*/
@UiThread
@NonNull
@@ -1323,7 +1323,7 @@ public ICancelable commitActivation(
@NonNull String title,
@NonNull String description,
@NonNull final Password password,
- final @NonNull IPersistActivationWithBiometryListener callback) {
+ final @NonNull IPersistActivationWithBiometricsListener callback) {
return persistActivation(context, fragmentActivity, title, description, password, callback);
}
@@ -2067,7 +2067,7 @@ private ICancelable addBiometryFactorImpl(
public void onFetchEncryptedVaultUnlockKeySucceed(final String encryptedEncryptionKey) {
if (encryptedEncryptionKey != null) {
// Authenticate using biometry to generate a key
- final ICancelable biometricAuthentication = authenticateUsingBiometry(context, fragmentHelper, title, description, true, new IBiometricAuthenticationCallback() {
+ final ICancelable biometricAuthentication = authenticateUsingBiometrics(context, fragmentHelper, title, description, true, new IBiometricAuthenticationCallback() {
@Override
public void onBiometricDialogCancelled(boolean userCancel) {
if (userCancel) {
@@ -2301,8 +2301,78 @@ public void onCancel() {
}
/**
- * Authenticate a client using biometric authentication. In case of the authentication is successful and {@link IBiometricAuthenticationCallback#onBiometricDialogSuccess(BiometricKeyData)} callback is called,
- * you can use {@code biometricKeyEncrypted} as a parameter to {@link PowerAuthAuthentication#useBiometry} property.
+ * Authenticate a client using biometric authentication. In case of the authentication is successful and
+ * {@link IAuthenticateWithBiometricsListener#onBiometricDialogSuccess(PowerAuthAuthentication)} callback is called.
+ *
+ * @param context Context.
+ * @param fragment The fragment of the application that will host the prompt.
+ * @param title Dialog title.
+ * @param description Dialog description.
+ * @param listener Callback with the authentication result.
+ * @return {@link ICancelable} object associated with the biometric prompt.
+ */
+ @UiThread
+ @NonNull
+ public ICancelable authenticateUsingBiometrics(
+ @NonNull Context context,
+ @NonNull Fragment fragment,
+ @NonNull String title,
+ @NonNull String description,
+ final @NonNull IAuthenticateWithBiometricsListener listener) {
+ return authenticateUsingBiometrics(context, FragmentHelper.from(fragment), title, description, false, getBiometricCallbackWithListener(listener));
+ }
+
+ /**
+ * Authenticate a client using biometric authentication. In case of the authentication is successful and
+ * {@link IAuthenticateWithBiometricsListener#onBiometricDialogSuccess(PowerAuthAuthentication)} callback is called.
+ *
+ * @param context Context.
+ * @param fragmentActivity The activity of the application that will host the prompt.
+ * @param title Dialog title.
+ * @param description Dialog description.
+ * @param listener Callback with the authentication result.
+ * @return {@link ICancelable} object associated with the biometric prompt.
+ */
+ @UiThread
+ @NonNull
+ public ICancelable authenticateUsingBiometrics(
+ @NonNull Context context,
+ @NonNull FragmentActivity fragmentActivity,
+ @NonNull String title,
+ @NonNull String description,
+ final @NonNull IAuthenticateWithBiometricsListener listener) {
+ return authenticateUsingBiometrics(context, FragmentHelper.from(fragmentActivity), title, description, false, getBiometricCallbackWithListener(listener));
+ }
+
+ /**
+ * Create low level biometric authentication callback that bridge the result to the provided listener.
+ * @param listener Target listener.
+ * @return Instance of {@link IBiometricAuthenticationCallback}.
+ */
+ @NonNull
+ private IBiometricAuthenticationCallback getBiometricCallbackWithListener(@NonNull IAuthenticateWithBiometricsListener listener) {
+ return new IBiometricAuthenticationCallback() {
+ @Override
+ public void onBiometricDialogCancelled(boolean userCancel) {
+ listener.onBiometricDialogCancelled(userCancel);
+ }
+
+ @Override
+ public void onBiometricDialogSuccess(@NonNull BiometricKeyData biometricKeyData) {
+ final PowerAuthAuthentication authentication = PowerAuthAuthentication.possessionWithBiometry(biometricKeyData.getDerivedData());
+ listener.onBiometricDialogSuccess(authentication);
+ }
+
+ @Override
+ public void onBiometricDialogFailed(@NonNull PowerAuthErrorException error) {
+ listener.onBiometricDialogFailed(error);
+ }
+ };
+ }
+
+ /**
+ * Authenticate a client using biometric authentication. In case of the authentication is successful and
+ * {@link IBiometricAuthenticationCallback#onBiometricDialogSuccess(BiometricKeyData)} callback is called.
*
* @param context Context.
* @param fragment The fragment of the application that will host the prompt.
@@ -2310,21 +2380,23 @@ public void onCancel() {
* @param description Dialog description.
* @param callback Callback with the authentication result.
* @return {@link ICancelable} object associated with the biometric prompt.
+ * @deprecated Use {@link #authenticateUsingBiometrics(Context, Fragment, String, String, IAuthenticateWithBiometricsListener)} as a replacement.
*/
@UiThread
@NonNull
+ @Deprecated // 1.8.0
public ICancelable authenticateUsingBiometry(
@NonNull Context context,
@NonNull Fragment fragment,
@NonNull String title,
@NonNull String description,
final @NonNull IBiometricAuthenticationCallback callback) {
- return authenticateUsingBiometry(context, FragmentHelper.from(fragment), title, description, false, callback);
+ return authenticateUsingBiometrics(context, FragmentHelper.from(fragment), title, description, false, callback);
}
/**
- * Authenticate a client using biometric authentication. In case of the authentication is successful and {@link IBiometricAuthenticationCallback#onBiometricDialogSuccess(BiometricKeyData)} callback is called,
- * you can use {@code biometricKeyEncrypted} as a parameter to {@link PowerAuthAuthentication#useBiometry} property.
+ * Authenticate a client using biometric authentication. In case of the authentication is successful and
+ * {@link IBiometricAuthenticationCallback#onBiometricDialogSuccess(BiometricKeyData)} callback is called,
*
* @param context Context.
* @param fragmentActivity The activity of the application that will host the prompt.
@@ -2332,21 +2404,22 @@ public ICancelable authenticateUsingBiometry(
* @param description Dialog description.
* @param callback Callback with the authentication result.
* @return {@link ICancelable} object associated with the biometric prompt.
+ * @deprecated Use {@link #authenticateUsingBiometrics(Context, FragmentActivity, String, String, IAuthenticateWithBiometricsListener)} as a replacement.
*/
@UiThread
@NonNull
+ @Deprecated // 1.8.0
public ICancelable authenticateUsingBiometry(
@NonNull Context context,
@NonNull FragmentActivity fragmentActivity,
@NonNull String title,
@NonNull String description,
final @NonNull IBiometricAuthenticationCallback callback) {
- return authenticateUsingBiometry(context, FragmentHelper.from(fragmentActivity), title, description, false, callback);
+ return authenticateUsingBiometrics(context, FragmentHelper.from(fragmentActivity), title, description, false, callback);
}
/**
- * Authenticate a client using biometric authentication. In case of the authentication is successful and {@link IBiometricAuthenticationCallback#onBiometricDialogSuccess(BiometricKeyData)} callback is called,
- * you can use {@code biometricKeyEncrypted} as a parameter to {@link PowerAuthAuthentication#useBiometry} property.
+ * Authenticate a client using biometric authentication.
*
* @param context Context.
* @param fragmentHelper Fragment helper for the dialog.
@@ -2358,7 +2431,7 @@ public ICancelable authenticateUsingBiometry(
*/
@UiThread
@NonNull
- private ICancelable authenticateUsingBiometry(
+ private ICancelable authenticateUsingBiometrics(
final @NonNull Context context,
final @NonNull FragmentHelper fragmentHelper,
final @NonNull String title,
diff --git a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/impl/KVHelper.java b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/impl/KVHelper.java
index 1fcc24a1..e22781c5 100644
--- a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/impl/KVHelper.java
+++ b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/impl/KVHelper.java
@@ -34,18 +34,35 @@
*/
public class KVHelper