From 6d24652b0a635f536190b4c731bf705acd263aa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraj=20=C4=8Eurech?= Date: Wed, 22 Jan 2025 14:15:05 +0100 Subject: [PATCH 1/2] iOS: Added `PowerAuthBiometricConfiguration` class #658 - Deprecated PowerAuthKeychainConfiguration properties moved to new biometric configuration - Deprecate shared instances --- docs/Migration-from-1.9-to-1.10.md | 16 ++- docs/PowerAuth-SDK-for-iOS.md | 65 +++++---- .../PowerAuth2.xcodeproj/project.pbxproj | 12 ++ .../PowerAuthBiometricConfiguration.h | 44 +++++++ .../PowerAuthBiometricConfiguration.m | 73 +++++++++++ .../PowerAuth2/PowerAuthClientConfiguration.h | 4 +- .../PowerAuthKeychainConfiguration.h | 27 +++- proj-xcode/PowerAuth2/PowerAuthSDK.h | 70 ++++++++-- proj-xcode/PowerAuth2/PowerAuthSDK.m | 124 ++++++++++++++---- .../PowerAuth2/private/PowerAuthSDK+Private.h | 13 +- .../PowerAuth2/private/PowerAuthSDK+Private.m | 15 --- .../PowerAuthSDKDefaultTests.h | 1 + .../PowerAuthSDKDefaultTests.m | 11 +- .../PowerAuthSDKSharedTests.m | 2 + .../PowerAuthSdkTestHelper.h | 3 +- .../PowerAuthSdkTestHelper.m | 27 ++-- .../PowerAuthTokenTests.m | 5 +- 17 files changed, 401 insertions(+), 111 deletions(-) create mode 100644 proj-xcode/PowerAuth2/PowerAuthBiometricConfiguration.h create mode 100644 proj-xcode/PowerAuth2/PowerAuthBiometricConfiguration.m diff --git a/docs/Migration-from-1.9-to-1.10.md b/docs/Migration-from-1.9-to-1.10.md index 3fb2ba48..17574e82 100644 --- a/docs/Migration-from-1.9-to-1.10.md +++ b/docs/Migration-from-1.9-to-1.10.md @@ -2,8 +2,8 @@ PowerAuth Mobile SDK in version `1.10.0` provides the following improvements: -- Removed support of activation by recovery code. - +- PowerAuth mobile SDK no longer supports activation by the recovery code. +- New `PowerAuthBiometricConfiguration` class that simplifies biometric configuration of `PowerAuthSDK` class. ### Compatibility with PowerAuth Server @@ -48,6 +48,18 @@ PowerAuth Mobile SDK in version `1.10.0` provides the following improvements: - The following methods in `PowerAuthSDK` class are deprecated: - `unsafeChangePassword(from:to:)` - use asynchronous `changePassword(from:to:callback:)` as a replacement. + - Constructor `PowerAuthSDK(configuration:keychainConfiguration:clientConfiguration:)` - use methods with `PowerAuthBiometricConfiguration` parameter instead. + +- All static methods for accessing a various shared instances are now deprecated: + - `PowerAuthSDK.initSharedInstance(...)` and `PowerAuthSDK.sharedInstance()` - To ensure better control and flexibility, manage the global instances within your application code. + - `PowerAuthClientConfiguration.sharedInstance()` - use a class constructor with no parameters if you want to create the default configuration. + - `PowerAuthKeychainConfiguration.sharedInstance()` - use a class constructor with no parameters if you want to create the default configuration. + +- The following properties in `PowerAuthKeychainConfiguration` class are now deprecated: + - `linkBiometricItemsToCurrentSet` - use new `PowerAuthBiometricConfiguration.invalidateBiometricFactorAfterChange` instead, with the same meaning. + - `allowBiometricAuthenticationFallbackToDevicePasscode` - use new `PowerAuthBiometricConfiguration.allowFallbackToDevicePasscode` instead, with the same meaning. + - `invalidateLocalAuthenticationContextAfterUse` - use new `PowerAuthBiometricConfiguration.invalidateLocalAuthenticationContextAfterUse` instead, with the same meaning. + - Be aware that if you provide both, `PowerAuthBiometricConfiguration` and `PowerAuthKeychainConfiguration` objects to initialize `PowerAuthSDK`, then the values from the biometric configuration takes precedence. - Due to removed support of recovery codes, the following classes and methods are no longer available: - Methods removed in `PowerAuthSDK`: diff --git a/docs/PowerAuth-SDK-for-iOS.md b/docs/PowerAuth-SDK-for-iOS.md index 31124f60..9b0bee01 100644 --- a/docs/PowerAuth-SDK-for-iOS.md +++ b/docs/PowerAuth-SDK-for-iOS.md @@ -158,6 +158,14 @@ The `PowerAuthConfiguration` has the following additional properties: - `disableAutomaticProtocolUpgrade` - If set to `true`, then automatic protocol upgrade is disabled. This option should be used only for the debugging purposes. - `keychainKey_Biometry` - Specifies the 'key' used to store the `PowerAuthSDK` instance’s biometry-related key in the biometry keychain. If not set, the `instanceId` is applied. Do not alter this configuration unless you have a valid reason to do so. +### Biometric configuration + +The `PowerAuthBiometricConfiguration` object configures biometric authentication in the `PowerAuthSDK` object. It has the following configuration properties: + +- `invalidateBiometricFactorAfterChange` - If set to `true`, the biometric factor is invalidated when fingers are added or removed for Touch ID, or when the user re-enrolls for Face ID. The default value is false (i.e., changing biometric settings in the system does not invalidate the biometric factor). +- `allowFallbackToDevicePasscode` - If set to `true`, an item protected with biometry can also be accessed using the device passcode. If enabled, the `invalidateBiometricFactorAfterChange` option has no effect. The default value is `false`, meaning fallback to the device passcode is not enabled. +- `invalidateLocalAuthenticationContextAfterUse` - If set to `true`, the `LAContext` object provided by the application is invalidated after use in the SDK. The default value is true, meaning `LAContext` cannot be reused for retrieving keys protected with biometry. + ### HTTP client configuration The `PowerAuthClientConfiguration` object contains configuration for a HTTP client used internally by `PowerAuthSDK` object. It has the following configuration properties: @@ -169,21 +177,27 @@ The `PowerAuthClientConfiguration` object contains configuration for a HTTP clie ### Keychain configuration -The `PowerAuthKeychainConfiguration` object contains configuration for a keychain-based storage used by `PowerAuthSDK` class internally. The configuration contains the following properties: +The `PowerAuthKeychainConfiguration` object contains configuration for a keychain-based storage used by `PowerAuthSDK` class internally. -- `keychainAttribute_AccessGroup` - Property that specifies a keychain access group in case that keychain is shared between multiple applications or between application and its extensions. -- `keychainAttribute_UserDefaultsSuiteName` - Property that specifies the name of `UserDefaults` suite to store the flag indicating that application has been re-installed. If the value is not provided, then `UserDefaults.standardUserDefaults` suite is used. -- `linkBiometricItemsToCurrentSet` - If set, then the item protected with the biometry is invalidated if fingers are added or removed for Touch ID, or if the user re-enrolls for Face ID. The default value is `false` (e.g. changing biometry in the system doesn't invalidate the entry) -- `allowBiometricAuthenticationFallbackToDevicePasscode` - If set to `true`, then the item protected with the biometry can be accessed also with a device passcode. If set, then `linkBiometricItemsToCurrentSet` option has no effect. The default is `false`, so fallback to device's passcode is not enabled. -- `invalidateLocalAuthenticationContextAfterUse` - If set to `true`, then the `LAContext` object provided by application is invalidated after the use in SDK. The default value is `true`, so `LAContext` cannot be reused for getting keys protected with biometry. + +We strongly discourage you from altering the properties in this configuration unless you know what you are doing. + -The following properties are also available for configuration but are not recommended to be altered under typical circumstances, as changing them may impact the library’s stability or intended behavior: +The configuration contains the following properties: - `keychainInstanceName_Status` - Property that specifies the name of the Keychain service used to store statuses for different PowerAuth instances. You should not change this property unless you have a valid reason to do so. - `keychainInstanceName_Possession` - Property that specifies the name of the Keychain service used to store possession factor related key (one value for all `PowerAuthSDK` instances). - `keychainInstanceName_Biometry` - Property that specifies the name of the Keychain service used to store biometry related keys for different `PowerAuthSDK` instances. - `keychainInstanceName_TokenStore` - Property that specifies the name of the Keychain service used to store content of `PowerAuthToken` objects. -- `keychainKey_Possession` - Property that specifies a storage key used to store possession fator related key in an associated possession Keychain service. +- `keychainKey_Possession` - Property that specifies a storage key used to store possession factor related key in an associated possession Keychain service. + +The following properties are defined, but deprecated: + +- `keychainAttribute_AccessGroup` - Property that specifies a keychain access group in case that keychain is shared between multiple applications or between application and its extensions. Use configuration for [Activation Data Sharing](#configure-activation-data-sharing) instead. +- `keychainAttribute_UserDefaultsSuiteName` - Property that specifies the name of `UserDefaults` suite to store the flag indicating that application has been re-installed. If the value is not provided, then `UserDefaults.standardUserDefaults` suite is used. Use configuration for [Activation Data Sharing](#configure-activation-data-sharing) instead. +- `linkBiometricItemsToCurrentSet` - If set, then the item protected with the biometry is invalidated if fingers are added or removed for Touch ID, or if the user re-enrolls for Face ID. The default value is `false` (e.g. changing biometry in the system doesn't invalidate the entry). Use `PowerAuthBiometricConfiguration.invalidateBiometricFactorAfterChange` instead. +- `allowBiometricAuthenticationFallbackToDevicePasscode` - If set to `true`, then the item protected with the biometry can be accessed also with a device passcode. If set, then `linkBiometricItemsToCurrentSet` option has no effect. The default is `false`, so fallback to device's passcode is not enabled. Use `PowerAuthBiometricConfiguration.allowFallbackToDevicePasscode` instead. +- `invalidateLocalAuthenticationContextAfterUse` - If set to `true`, then the `LAContext` object provided by application is invalidated after the use in SDK. The default value is `true`, so `LAContext` cannot be reused for getting keys protected with biometry. Use `PowerAuthBiometricConfiguration.invalidateLocalAuthenticationContextAfterUse` instead. ## Activation @@ -1037,20 +1051,20 @@ powerAuthSDK.authenticateUsingBiometry(withPrompt: "Authenticate to sign in") { ### Biometry Factor-Related Key Lifetime -By default, the biometry factor-related key is **NOT** invalidated after the biometry enrolled in the system is changed. For example, if the user adds or removes the finger or enrolls with a new face, then the biometry factor-related key is still available for the signing operation. To change this behavior, you have to provide the `PowerAuthKeychainConfiguration` object with the `linkBiometricItemsToCurrentSet` parameter set to `true` and use that configuration for the `PowerAuthSDK` instance construction: +By default, the biometry factor-related key is **NOT** invalidated after the biometry enrolled in the system is changed. For example, if the user adds or removes the finger or enrolls with a new face, then the biometry factor-related key is still available for the signing operation. To change this behavior, you have to provide the `PowerAuthBiometricConfiguration` object with the `invalidateBiometricFactorAfterChange` parameter set to `true` and use that configuration for the `PowerAuthSDK` instance construction: ```swift // Prepare your PA config let configuration = PowerAuthConfiguration() // ... -// Prepare PowerAuthKeychainConfiguration -// Set true to the 'linkBiometricItemsToCurrentSet' property. -let keychainConfiguration = PowerAuthKeychainConfiguration() -keychainConfiguration.linkBiometricItemsToCurrentSet = true +// Prepare PowerAuthBiometricConfiguration +// Set true to the 'invalidateBiometricFactorAfterChange' property. +let biometricConfiguration = PowerAuthBiometricConfiguration() +biometricConfiguration.invalidateBiometricFactorAfterChange = true // Init PowerAuthSDK instance -let powerAuthSDK = PowerAuthSDK(configuration: configuration, keychainConfiguration: keychainConfiguration, clientConfiguration: nil) +let powerAuthSDK = PowerAuthSDK(configuration: configuration, biometricConfiguration: biometricConfiguration, clientConfiguration: nil) ``` @@ -1059,23 +1073,23 @@ Be aware that the configuration above is effective only for the new keys. So, if ### Fallback biometry to device passcode -By default, the fallback from biometric authentication to authenticate with the device's passcode is not allowed. To change this behavior, you have to provide the `PowerAuthKeychainConfiguration` object with the `allowBiometricAuthenticationFallbackToDevicePasscode` parameter set to `true` and use that configuration for the `PowerAuthSDK` instance construction: +By default, the fallback from biometric authentication to authenticate with the device's passcode is not allowed. To change this behavior, you have to provide the `PowerAuthBiometricConfiguration` object with the `allowFallbackToDevicePasscode` parameter set to `true` and use that configuration for the `PowerAuthSDK` instance construction: ```swift // Prepare your PA config let configuration = PowerAuthConfiguration() // ... -// Prepare PowerAuthKeychainConfiguration -// Set true to the 'allowBiometricAuthenticationFallbackToDevicePasscode' property. -let keychainConfiguration = PowerAuthKeychainConfiguration() -keychainConfiguration.allowBiometricAuthenticationFallbackToDevicePasscode = true +// Prepare PowerAuthBiometricConfiguration +// Set true to the 'allowFallbackToDevicePasscode' property. +let biometricConfiguration = PowerAuthBiometricConfiguration() +biometricConfiguration.allowFallbackToDevicePasscode = true // Init PowerAuthSDK instance -let powerAuthSDK = PowerAuthSDK(configuration: configuration, keychainConfiguration: keychainConfiguration, clientConfiguration: nil) +let powerAuthSDK = PowerAuthSDK(configuration: configuration, biometricConfiguration: biometricConfiguration, clientConfiguration: nil) ``` -Once the configuration above is used, then the `linkBiometricItemsToCurrentSet` option does not affect the biometry factor-related key lifetime. +Once the configuration above is used, then the `invalidateBiometricFactorAfterChange` option does not affect the biometry factor-related key lifetime. It's not recommended to allow fallback to device passcodes if your application falls under EU banking regulations or your application needs to distinguish between the biometric and the knowledge-factor-based signatures. This is because if the biometry factor-related key is unlocked with the device's passcode, then it's no longer a biometric signature. @@ -1118,7 +1132,7 @@ If you plan to pre-authorize `LAContext` and use it for multiple biometry signat - Multiple signatures in a row could be problematic if your application falls under EU banking regulations. - It would be difficult to prove that the user authorized the request if your application contains a bug and does the signature on the user's behalf or with the wrong context. -If you still insist to re-use `LAContext` then you have to alter `PowerAuthKeychainConfiguration` and set `invalidateLocalAuthenticationContextAfterUse` to `false`. +If you still insist to re-use `LAContext` then you have to alter `PowerAuthBiometricConfiguration` and set `invalidateLocalAuthenticationContextAfterUse` to `false`. ## Biometry troubleshooting @@ -1960,16 +1974,13 @@ Here's the list of important error codes, which the application should properly Sometimes, you may need to develop or test your application against a service that runs over HTTPS protocol with an invalid (self-signed) SSL certificate. By default, the HTTP client used in PowerAuth SDK communication validates the certificate. To disable the certificate validation, add the following code just before your `PowerAuthSDK` instance configuration: ```swift -// Set `PowerAuthClientSslNoValidationStrategy as the default client SSL certificate validation strategy` -PowerAuthClientConfiguration.sharedInstance().sslValidationStrategy = PowerAuthClientSslNoValidationStrategy() - -// In case you're setting the `PowerAuthClientConfiguration` explicitly, use: +// Create a custom `PowerAuthClientConfiguration` object: let clientConfig = PowerAuthClientConfiguration() clientConfig.sslValidationStrategy = PowerAuthClientSslNoValidationStrategy() // configure the PowerAuthSDK object let powerAuthSDK = PowerAuthSDK( configuration: PowerAuthConfiguration(...), - keychainConfiguration: nil, // optional, default will be used when nil + biometricConfiguration: nil, // optional, default will be used when nil clientConfiguration: clientConfig ) ``` diff --git a/proj-xcode/PowerAuth2.xcodeproj/project.pbxproj b/proj-xcode/PowerAuth2.xcodeproj/project.pbxproj index 69178f14..d9e381c5 100644 --- a/proj-xcode/PowerAuth2.xcodeproj/project.pbxproj +++ b/proj-xcode/PowerAuth2.xcodeproj/project.pbxproj @@ -434,6 +434,10 @@ BFEC963A21B6932D00FB5165 /* PowerAuthBasicHttpAuthenticationRequestInterceptor.h in Headers */ = {isa = PBXBuildFile; fileRef = BFEC963821B6932D00FB5165 /* PowerAuthBasicHttpAuthenticationRequestInterceptor.h */; settings = {ATTRIBUTES = (Public, ); }; }; BFEC963B21B6932D00FB5165 /* PowerAuthBasicHttpAuthenticationRequestInterceptor.m in Sources */ = {isa = PBXBuildFile; fileRef = BFEC963921B6932D00FB5165 /* PowerAuthBasicHttpAuthenticationRequestInterceptor.m */; }; BFEC963E21B6990400FB5165 /* PowerAuthCustomHeaderRequestInterceptor.h in Headers */ = {isa = PBXBuildFile; fileRef = BFEC963C21B6990400FB5165 /* PowerAuthCustomHeaderRequestInterceptor.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BFF6045A2D3FFCDC0053C3E6 /* PowerAuthBiometricConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = BFF604592D3FFCDC0053C3E6 /* PowerAuthBiometricConfiguration.m */; }; + BFF6045B2D3FFCDC0053C3E6 /* PowerAuthBiometricConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = BFF604582D3FFCDC0053C3E6 /* PowerAuthBiometricConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BFF6045C2D3FFCDC0053C3E6 /* PowerAuthBiometricConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = BFF604582D3FFCDC0053C3E6 /* PowerAuthBiometricConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BFF6045D2D3FFCDC0053C3E6 /* PowerAuthBiometricConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = BFF604592D3FFCDC0053C3E6 /* PowerAuthBiometricConfiguration.m */; }; BFF6716B243376B0009279CA /* PowerAuthActivation.h in Headers */ = {isa = PBXBuildFile; fileRef = BFF67169243376B0009279CA /* PowerAuthActivation.h */; settings = {ATTRIBUTES = (Public, ); }; }; BFF6716C243376B0009279CA /* PowerAuthActivation.m in Sources */ = {isa = PBXBuildFile; fileRef = BFF6716A243376B0009279CA /* PowerAuthActivation.m */; }; BFF711DE265D4FC800DB696A /* PowerAuthWCSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = BFA0E2A1200D12420011F0A6 /* PowerAuthWCSessionManager.m */; }; @@ -823,6 +827,8 @@ BFEC963921B6932D00FB5165 /* PowerAuthBasicHttpAuthenticationRequestInterceptor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PowerAuthBasicHttpAuthenticationRequestInterceptor.m; sourceTree = ""; }; BFEC963C21B6990400FB5165 /* PowerAuthCustomHeaderRequestInterceptor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PowerAuthCustomHeaderRequestInterceptor.h; sourceTree = ""; }; BFEC963D21B6990400FB5165 /* PowerAuthCustomHeaderRequestInterceptor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PowerAuthCustomHeaderRequestInterceptor.m; sourceTree = ""; }; + BFF604582D3FFCDC0053C3E6 /* PowerAuthBiometricConfiguration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PowerAuthBiometricConfiguration.h; sourceTree = ""; }; + BFF604592D3FFCDC0053C3E6 /* PowerAuthBiometricConfiguration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PowerAuthBiometricConfiguration.m; sourceTree = ""; }; BFF67169243376B0009279CA /* PowerAuthActivation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PowerAuthActivation.h; sourceTree = ""; }; BFF6716A243376B0009279CA /* PowerAuthActivation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PowerAuthActivation.m; sourceTree = ""; }; BFFB0B9B2167A541004A06E2 /* PA2CreateActivationRequestData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PA2CreateActivationRequestData.h; sourceTree = ""; }; @@ -1023,6 +1029,8 @@ 1B0A239C1D6C6F9E00D24167 /* PowerAuthConfiguration.m */, BF8BFBEA215BBA7B001D6852 /* PowerAuthClientConfiguration.h */, BF8BFBEB215BBA7B001D6852 /* PowerAuthClientConfiguration.m */, + BFF604582D3FFCDC0053C3E6 /* PowerAuthBiometricConfiguration.h */, + BFF604592D3FFCDC0053C3E6 /* PowerAuthBiometricConfiguration.m */, BF28AA2427EC710F00FBFCEB /* PowerAuthSharingConfiguration.h */, BF28AA2527EC710F00FBFCEB /* PowerAuthSharingConfiguration.m */, BFF67169243376B0009279CA /* PowerAuthActivation.h */, @@ -1492,6 +1500,7 @@ BF38903A2A97861100D7A18E /* PA2TimeSynchronizationService.h in Headers */, BFFECE972636EC41001DA7A9 /* PowerAuthSessionStatusProvider.h in Headers */, BFFECEB926383CF7001DA7A9 /* PowerAuthDeprecated.h in Headers */, + BFF6045C2D3FFCDC0053C3E6 /* PowerAuthBiometricConfiguration.h in Headers */, BF5EB53124C85FE200F9DDB2 /* PowerAuthSDK.h in Headers */, BF08256D2631670D00B34E24 /* PowerAuthActivationResult.h in Headers */, BF5EB53224C85FE200F9DDB2 /* PowerAuthRestApiError.h in Headers */, @@ -1612,6 +1621,7 @@ BF02103F2164C67F009745A2 /* PA2HttpClient.h in Headers */, BFD3896F2C62640F0087F96D /* PA2GetTemporaryKeyRequest.h in Headers */, BF8CF50D2032EB41002A6B6E /* PA2PrivateMacros.h in Headers */, + BFF6045B2D3FFCDC0053C3E6 /* PowerAuthBiometricConfiguration.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2042,6 +2052,7 @@ BF28AA1A27EB1EDE00FBFCEB /* PA2SessionDataProvider.m in Sources */, BF28AA2F27ECA34900FBFCEB /* PowerAuthExternalPendingOperation.m in Sources */, BF5EB4C824C85FE200F9DDB2 /* PowerAuthBasicHttpAuthenticationRequestInterceptor.m in Sources */, + BFF6045D2D3FFCDC0053C3E6 /* PowerAuthBiometricConfiguration.m in Sources */, BF5EB4C924C85FE200F9DDB2 /* PowerAuthConfiguration.m in Sources */, BF5EB4CA24C85FE200F9DDB2 /* PA2PrivateEncryptorFactory.m in Sources */, BF5EB4CC24C85FE200F9DDB2 /* PA2CreateActivationResponseData.m in Sources */, @@ -2203,6 +2214,7 @@ BFADA9C921B6C0CE001C6EC2 /* PowerAuthCustomHeaderRequestInterceptor.m in Sources */, BF8CF4BD2032EB41002A6B6E /* PowerAuthRestApiErrorResponse.m in Sources */, BF28AA0D27EA09F000FBFCEB /* PA2SharedSessionInterface.m in Sources */, + BFF6045A2D3FFCDC0053C3E6 /* PowerAuthBiometricConfiguration.m in Sources */, BF0210442164D403009745A2 /* PA2AsyncOperation.m in Sources */, BF8CF4BE2032EB41002A6B6E /* PA2PrivateHttpTokenProvider.m in Sources */, BF8CF4C02032EB41002A6B6E /* PA2EncryptedRequest.m in Sources */, diff --git a/proj-xcode/PowerAuth2/PowerAuthBiometricConfiguration.h b/proj-xcode/PowerAuth2/PowerAuthBiometricConfiguration.h new file mode 100644 index 00000000..ff01b94c --- /dev/null +++ b/proj-xcode/PowerAuth2/PowerAuthBiometricConfiguration.h @@ -0,0 +1,44 @@ +/* + * Copyright 2025 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. + */ + +#import + +/** + The `PowerAuthBiometricConfiguration` object contains biometric configuration for `PowerAuthSDK` class. + */ +@interface PowerAuthBiometricConfiguration : NSObject + +/** + If set to `true`, then the biometric factor key is invalidated if fingers are added or removed + for Touch ID, or if the user re-enrolls for Face ID. The default value is `false` (e.g. changing biometry + in the system doesn't invalidate the factor key) + */ +@property (nonatomic, assign) BOOL invalidateBiometricFactorAfterChange; + +/** + If set to `true`, then the item protected with the biometry can be accessed also with a device passcode. + If set, then `linkBiometricItemsToCurrentSet` option has no effect. The default is NO, so fallback + to device's passcode is not enabled. + */ +@property (nonatomic, assign) BOOL allowFallbackToDevicePasscode; + +/** + If set to YES, then the `LAContext` object provided by application is invalidated after the use in SDK. + The default value is YES, so `LAContext` cannot be reused for getting keys protected with biometry. + */ +@property (nonatomic, assign) BOOL invalidateLocalAuthenticationContextAfterUse; + +@end diff --git a/proj-xcode/PowerAuth2/PowerAuthBiometricConfiguration.m b/proj-xcode/PowerAuth2/PowerAuthBiometricConfiguration.m new file mode 100644 index 00000000..ced43f4f --- /dev/null +++ b/proj-xcode/PowerAuth2/PowerAuthBiometricConfiguration.m @@ -0,0 +1,73 @@ +/* + * Copyright 2025 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. + */ + +#import "PowerAuthBiometricConfiguration.h" +#import "PowerAuthKeychainConfiguration.h" +#import "PowerAuthKeychain.h" + +@implementation PowerAuthBiometricConfiguration + +- (instancetype) init +{ + self = [super init]; + if (self) { + _invalidateBiometricFactorAfterChange = NO; + _allowFallbackToDevicePasscode = NO; + _invalidateLocalAuthenticationContextAfterUse = YES; + } + return self; +} + +- (id) copyWithZone:(NSZone *)zone +{ + PowerAuthBiometricConfiguration * c = [[self.class allocWithZone:zone] init]; + if (c) { + c->_invalidateBiometricFactorAfterChange = _invalidateBiometricFactorAfterChange; + c->_allowFallbackToDevicePasscode = _allowFallbackToDevicePasscode; + c->_invalidateLocalAuthenticationContextAfterUse = _invalidateLocalAuthenticationContextAfterUse; + } + return c; +} + +- (PowerAuthKeychainItemAccess) biometricItemAccess +{ + if (self.allowFallbackToDevicePasscode) { + return PowerAuthKeychainItemAccess_AnyBiometricSetOrDevicePasscode; + } else if (self.invalidateBiometricFactorAfterChange) { + return PowerAuthKeychainItemAccess_CurrentBiometricSet; + } else { + return PowerAuthKeychainItemAccess_AnyBiometricSet; + } +} + + +// Compatibility due to PA2_DEPRECATED(1.10.0), remove constructor in 2.0.0 + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +- (instancetype) initWithKeychainConfiguration:(PowerAuthKeychainConfiguration*)keychainConfiguration +{ + self = [super init]; + if (self) { + _invalidateBiometricFactorAfterChange = keychainConfiguration.linkBiometricItemsToCurrentSet; + _allowFallbackToDevicePasscode = keychainConfiguration.allowBiometricAuthenticationFallbackToDevicePasscode; + _invalidateLocalAuthenticationContextAfterUse = keychainConfiguration.invalidateLocalAuthenticationContextAfterUse; + } + return self; +} +#pragma clang diagnostic pop + +@end diff --git a/proj-xcode/PowerAuth2/PowerAuthClientConfiguration.h b/proj-xcode/PowerAuth2/PowerAuthClientConfiguration.h index 4b21f7ed..d46ef25f 100644 --- a/proj-xcode/PowerAuth2/PowerAuthClientConfiguration.h +++ b/proj-xcode/PowerAuth2/PowerAuthClientConfiguration.h @@ -88,8 +88,10 @@ /** Return the shared in stance of a client configuration object. + This static method is deprecated. Please use the configuration object created and managed by your application. + @return Shared instance of a client configuration. */ -+ (nonnull instancetype) sharedInstance; ++ (nonnull instancetype) sharedInstance PA2_DEPRECATED(1.10.0); @end diff --git a/proj-xcode/PowerAuth2/PowerAuthKeychainConfiguration.h b/proj-xcode/PowerAuth2/PowerAuthKeychainConfiguration.h index ea8e18ce..d7581d3d 100644 --- a/proj-xcode/PowerAuth2/PowerAuthKeychainConfiguration.h +++ b/proj-xcode/PowerAuth2/PowerAuthKeychainConfiguration.h @@ -58,8 +58,10 @@ extern NSString * __nonnull const PowerAuthKeychainKey_Possession; /** Access group name used by the PowerAuthSDK keychain instances. + + The property is deprecated, you can use `PowerAuthSharingConfiguration` to properly configure activation data sharing between applications and extensions. */ -@property (nonatomic, strong, nullable) NSString *keychainAttribute_AccessGroup; +@property (nonatomic, strong, nullable) NSString *keychainAttribute_AccessGroup PA2_DEPRECATED(1.10.0); /** Suite name used by the NSUserDefaults that check for Keychain data presence. @@ -67,8 +69,10 @@ extern NSString * __nonnull const PowerAuthKeychainKey_Possession; If the value is not set, `standardUserDefaults` are used. Otherwise, user defaults with given suite name are created. In case a developer started using SDK with no suite name specified, the developer is responsible for migrating data to the new `NSUserDefaults` before using the SDK with the new suite name. + + The property is deprecated, you can use `PowerAuthSharingConfiguration` to properly configure activation data sharing between applications and extensions. */ -@property (nonatomic, strong, nullable) NSString *keychainAttribute_UserDefaultsSuiteName; +@property (nonatomic, strong, nullable) NSString *keychainAttribute_UserDefaultsSuiteName PA2_DEPRECATED(1.10.0); /** Name of the Keychain service used to store statuses for different PowerAuth instances. @@ -98,26 +102,37 @@ extern NSString * __nonnull const PowerAuthKeychainKey_Possession; If set, then the item protected with the biometry is invalidated if fingers are added or removed for Touch ID, or if the user re-enrolls for Face ID. The default value is NO (e.g. changing biometry in the system doesn't invalidate the entry) + + This property is deprecated. Please use `PowerAuthBiometricConfiguration.invalidateBiometricFactorAfterChange` instead. + If both configuration classes are used to initialize `PowerAuthSDK`, the value from `PowerAuthBiometricConfiguration` takes precedence. */ -@property (nonatomic, assign) BOOL linkBiometricItemsToCurrentSet; +@property (nonatomic, assign) BOOL linkBiometricItemsToCurrentSet PA2_DEPRECATED(1.10.0); /** If set to YES, then the item protected with the biometry can be accessed also with a device passcode. If set, then `linkBiometricItemsToCurrentSet` option has no effect. The default is NO, so fallback to device's passcode is not enabled. + + This property is deprecated. Please use `PowerAuthBiometricConfiguration.allowFallbackToDevicePasscode` instead. + If both configuration classes are used to initialize `PowerAuthSDK`, the value from `PowerAuthBiometricConfiguration` takes precedence. */ -@property (nonatomic, assign) BOOL allowBiometricAuthenticationFallbackToDevicePasscode; +@property (nonatomic, assign) BOOL allowBiometricAuthenticationFallbackToDevicePasscode PA2_DEPRECATED(1.10.0); /** If set to YES, then the LAContext object provided by application is invalidated after the use in SDK. The default value is YES, so `LAContext` cannot be reused for getting keys protected with biometry. + + This property is deprecated. Please use `PowerAuthBiometricConfiguration.invalidateLocalAuthenticationContextAfterUse` instead. + If both configuration classes are used to initialize `PowerAuthSDK`, the value from `PowerAuthBiometricConfiguration` takes precedence. */ -@property (nonatomic, assign) BOOL invalidateLocalAuthenticationContextAfterUse; +@property (nonatomic, assign) BOOL invalidateLocalAuthenticationContextAfterUse PA2_DEPRECATED(1.10.0); /** Return the shared in stance of a Keychain configuration object. + This static method is deprecated. Please use the configuration object created and managed by your application. + @return Shared instance of a Keychain configuration. */ -+ (nonnull instancetype) sharedInstance; ++ (nonnull instancetype) sharedInstance PA2_DEPRECATED(1.10.0); @end diff --git a/proj-xcode/PowerAuth2/PowerAuthSDK.h b/proj-xcode/PowerAuth2/PowerAuthSDK.h index 9f8253ed..46e21483 100644 --- a/proj-xcode/PowerAuth2/PowerAuthSDK.h +++ b/proj-xcode/PowerAuth2/PowerAuthSDK.h @@ -20,6 +20,7 @@ #import #import #import +#import #import #import #import @@ -64,6 +65,13 @@ doesn't affect this SDK instance. */ @property (nonatomic, strong, nonnull, readonly) PowerAuthClientConfiguration *clientConfiguration; +/** + Instance of `PowerAuthBiometricConfiguration` object, provided during the object initialization. + + Note that the copy of internal object is always returned and thus making changes to the returned object + doesn't affect this SDK instance. + */ +@property (nonatomic, strong, nonnull, readonly) PowerAuthBiometricConfiguration *biometricConfiguration; /** Instance of `PowerAuthKeychainConfiguration` object, provided during the object initialization. @@ -92,19 +100,35 @@ Creates an instance of SDK and initializes it with given configuration objects. @param configuration to be used for initialization. - @param keychainConfiguration to be used for keychain configuration. If nil is provided, then `PowerAuthKeychainConfiguration.sharedInstance()` is used. - @param clientConfiguration to be used for HTTP client configuration. If nil is provided, then `PowerAuthClientConfiguration.sharedInstance()` is used. + @param biometricConfiguration to be used for biometric configuration. If nil is provided, then the default configuration is applied. + @param clientConfiguration to be used for HTTP client configuration. If nil is provided, then the default configuration is applied. + @param keychainConfiguration to be used for keychain configuration. If nil is provided, then the default configuration is applied. @return Initialized instance. @exception NSException thrown in case configuration is not valid. */ -- (nullable instancetype) initWithConfiguration:(nonnull PowerAuthConfiguration *)configuration - keychainConfiguration:(nullable PowerAuthKeychainConfiguration *)keychainConfiguration - clientConfiguration:(nullable PowerAuthClientConfiguration *)clientConfiguration; +- (nonnull instancetype) initWithConfiguration:(nonnull PowerAuthConfiguration *)configuration + biometricConfiguration:(nullable PowerAuthBiometricConfiguration *)biometricConfiguration + clientConfiguration:(nullable PowerAuthClientConfiguration *)clientConfiguration + keychainConfiguration:(nullable PowerAuthKeychainConfiguration *)keychainConfiguration; + +/** + Creates an instance of SDK and initializes it with given configuration objects. + + @param configuration to be used for initialization. + @param biometricConfiguration to be used for biometric configuration. If nil is provided, then the default configuration is applied. + @param clientConfiguration to be used for HTTP client configuration. If nil is provided, then the default configuration is applied. + + @return Initialized instance. + @exception NSException thrown in case configuration is not valid. + */ +- (nonnull instancetype) initWithConfiguration:(nonnull PowerAuthConfiguration *)configuration + biometricConfiguration:(nullable PowerAuthBiometricConfiguration *)biometricConfiguration + clientConfiguration:(nullable PowerAuthClientConfiguration *)clientConfiguration; /** Creates an instance of SDK and initializes it with given configuration. - The appropriate shared configs are used for object's `clientConfiguration` and `keychainConfiguration` properties. + The default configs are used for object's biometric, keychain and client configurations. @param configuration to be used for initialization. @return Initialized instance. @@ -112,30 +136,56 @@ */ - (nullable instancetype) initWithConfiguration:(nonnull PowerAuthConfiguration *)configuration; +/** Creates an instance of SDK and initializes it with given configuration objects. + + This constructor is deprecated. Please use one of constructors that takes also `PowerAuthBiometricConfiguration` in parameter. + + @param configuration to be used for initialization. + @param keychainConfiguration to be used for keychain configuration. If nil is provided, then the default configuration is used. + @param clientConfiguration to be used for HTTP client configuration. If nil is provided, then the default configuration is used. + + @return Initialized instance. + @exception NSException thrown in case configuration is not valid. + */ +- (nullable instancetype) initWithConfiguration:(nonnull PowerAuthConfiguration *)configuration + keychainConfiguration:(nullable PowerAuthKeychainConfiguration *)keychainConfiguration + clientConfiguration:(nullable PowerAuthClientConfiguration *)clientConfiguration + PA2_DEPRECATED(1.10.0); + /** Creates a default shared instance and initializes it with given configuration. - The appropriate shared configs are used for shared instance's `clientConfiguration` and `keychainConfiguration` properties. + The appropriate default configs are used for shared instance's configuration properties. + + This static method is deprecated and will be removed in a future release. To ensure better control and flexibility, manage the global instance + of the `PowerAuthSDK` class within your application code. @param configuration to be used for initialization. */ -+ (void) initSharedInstance:(nonnull PowerAuthConfiguration *)configuration; ++ (void) initSharedInstance:(nonnull PowerAuthConfiguration *)configuration PA2_DEPRECATED(1.10.0); /** Creates a default shared instance and initializes it with given configuration objects. + This static method is deprecated and will be removed in a future release. To ensure better control and flexibility, manage the global instance + of the `PowerAuthSDK` class within your application code. + @param configuration to be used for initialization. @param keychainConfiguration to be used for keychain configuration. If nil is provided, then `PowerAuthKeychainConfiguration.sharedInstance()` is used. @param clientConfiguration to be used for HTTP client configuration. If nil is provided, then `PowerAuthClientConfiguration.sharedInstance()` is used. */ + (void) initSharedInstance:(nonnull PowerAuthConfiguration *)configuration keychainConfiguration:(nullable PowerAuthKeychainConfiguration *)keychainConfiguration - clientConfiguration:(nullable PowerAuthClientConfiguration *)clientConfiguration; + clientConfiguration:(nullable PowerAuthClientConfiguration *)clientConfiguration + PA2_DEPRECATED(1.10.0); /** Return the default shared instance of the PowerAuth SDK. + This static method is deprecated and will be removed in a future release. To ensure better control and flexibility, manage the global instance + of the `PowerAuthSDK` class within your application code. + @return Shared instance of the PowerAuth SDK. */ -+ (nonnull PowerAuthSDK*) sharedInstance; ++ (nonnull PowerAuthSDK*) sharedInstance PA2_DEPRECATED(1.10.0); /** Create a new activation. diff --git a/proj-xcode/PowerAuth2/PowerAuthSDK.m b/proj-xcode/PowerAuth2/PowerAuthSDK.m index 9c366ff7..7696a05d 100644 --- a/proj-xcode/PowerAuth2/PowerAuthSDK.m +++ b/proj-xcode/PowerAuth2/PowerAuthSDK.m @@ -59,6 +59,7 @@ @implementation PowerAuthSDK id _sessionInterface; PowerAuthCoreSession * _coreSession; PowerAuthConfiguration * _configuration; + PowerAuthBiometricConfiguration * _biometricConfiguration; PowerAuthKeychainConfiguration * _keychainConfiguration; PowerAuthClientConfiguration * _clientConfiguration; @@ -83,9 +84,29 @@ @implementation PowerAuthSDK #pragma mark - Private methods +/** + The private function returns biometric configuration created from the provided configurations. If application still provide the deprecated keychain configuration, + then the function constructs biometric configuration from the parameters provided in keychain configuration. If no configuration is provided, then returns the default + biometric configuration. + */ +static PowerAuthBiometricConfiguration * _BuildBiometricConfiguration(PowerAuthBiometricConfiguration * biometricConfiguration, PowerAuthKeychainConfiguration * keychainConfiguration) +{ + if (biometricConfiguration) { + return [biometricConfiguration copy]; + } + if (keychainConfiguration) { + return [[PowerAuthBiometricConfiguration alloc] initWithKeychainConfiguration:keychainConfiguration]; + } + return [[PowerAuthBiometricConfiguration alloc] init]; +} + +/** + Initialize instance of SDK object. The method should be called only from the object's constructor. + */ - (void) initializeWithConfiguration:(PowerAuthConfiguration*)configuration - keychainConfiguration:(PowerAuthKeychainConfiguration*)keychainConfiguration + biometricConfiguration:(PowerAuthBiometricConfiguration*)biometricConfiguration clientConfiguration:(PowerAuthClientConfiguration*)clientConfiguration + keychainConfiguration:(PowerAuthKeychainConfiguration*)keychainConfiguration { // Check if the configuration was nil @@ -103,19 +124,27 @@ - (void) initializeWithConfiguration:(PowerAuthConfiguration*)configuration // Make copy of configuration objects _configuration = [configuration copy]; - _keychainConfiguration = [(keychainConfiguration ? keychainConfiguration : [PowerAuthKeychainConfiguration sharedInstance]) copy]; - _clientConfiguration = [(clientConfiguration ? clientConfiguration : [PowerAuthClientConfiguration sharedInstance]) copy]; + _biometricConfiguration = _BuildBiometricConfiguration(biometricConfiguration, keychainConfiguration); + _keychainConfiguration = keychainConfiguration ? [keychainConfiguration copy] : [[PowerAuthKeychainConfiguration alloc] init]; + _clientConfiguration = clientConfiguration ? [clientConfiguration copy] : [[PowerAuthClientConfiguration alloc] init]; // Prepare identifier for biometry related keys - use instanceId by default, or a custom value if set _biometryKeyIdentifier = _configuration.keychainKey_Biometry ? _configuration.keychainKey_Biometry : _configuration.instanceId; // Alter keychain in case that PowerAuthSharingConfiguration is used PowerAuthSharingConfiguration * sharingConfiguration = _configuration.sharingConfiguration; - NSString * biometryKeychainAccessGroup = nil; + NSString * keychainAccessGroup = nil; + NSString * userDefaultsSuiteName = nil; if (sharingConfiguration != nil) { - _keychainConfiguration.keychainAttribute_UserDefaultsSuiteName = sharingConfiguration.appGroup; - _keychainConfiguration.keychainAttribute_AccessGroup = sharingConfiguration.keychainAccessGroup; - biometryKeychainAccessGroup = sharingConfiguration.keychainAccessGroup; + userDefaultsSuiteName = sharingConfiguration.appGroup; + keychainAccessGroup = sharingConfiguration.keychainAccessGroup; + } else if (_keychainConfiguration) { + // Using deprecated interfaces internally. + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" + userDefaultsSuiteName = _keychainConfiguration.keychainAttribute_UserDefaultsSuiteName; + keychainAccessGroup = _keychainConfiguration.keychainAttribute_AccessGroup; + #pragma clang diagnostic pop } // Prepare time synchronization sevice. // @@ -136,24 +165,27 @@ - (void) initializeWithConfiguration:(PowerAuthConfiguration*)configuration // Create a new keychain instances _statusKeychain = [[PowerAuthKeychain alloc] initWithIdentifier:_keychainConfiguration.keychainInstanceName_Status - accessGroup:_keychainConfiguration.keychainAttribute_AccessGroup]; + accessGroup:keychainAccessGroup]; _sharedKeychain = [[PowerAuthKeychain alloc] initWithIdentifier:_keychainConfiguration.keychainInstanceName_Possession - accessGroup:_keychainConfiguration.keychainAttribute_AccessGroup]; + accessGroup:keychainAccessGroup]; _biometryOnlyKeychain = [[PowerAuthKeychain alloc] initWithIdentifier:_keychainConfiguration.keychainInstanceName_Biometry - accessGroup:biometryKeychainAccessGroup]; + accessGroup:keychainAccessGroup]; // Initialize token store with its own keychain as a backing storage and remote token provider. PowerAuthKeychain * tokenStoreKeychain = [[PowerAuthKeychain alloc] initWithIdentifier:_keychainConfiguration.keychainInstanceName_TokenStore - accessGroup:_keychainConfiguration.keychainAttribute_AccessGroup]; + accessGroup:keychainAccessGroup]; // Make sure to reset keychain data after app re-install. // Important: This deletes all Keychain data in all PowerAuthSDK instances! // By default, the code uses standard user defaults, use `PowerAuthKeychainConfiguration.keychainAttribute_UserDefaultsSuiteName` to use `NSUserDefaults` with a custom suite name. NSUserDefaults *userDefaults = nil; - if (_keychainConfiguration.keychainAttribute_UserDefaultsSuiteName != nil) { - userDefaults = [[NSUserDefaults alloc] initWithSuiteName:_keychainConfiguration.keychainAttribute_UserDefaultsSuiteName]; + if (userDefaultsSuiteName) { + userDefaults = [[NSUserDefaults alloc] initWithSuiteName:userDefaultsSuiteName]; } else { userDefaults = [NSUserDefaults standardUserDefaults]; } + if (!userDefaults) { + [PowerAuthSDK throwInvalidConfigurationException]; + } if ([userDefaults boolForKey:PowerAuthKeychain_Initialized] == NO) { [_statusKeychain deleteAllData]; [_sharedKeychain deleteAllData]; @@ -260,6 +292,11 @@ - (PowerAuthClientConfiguration*) clientConfiguration return [_clientConfiguration copy]; } +- (PowerAuthBiometricConfiguration*) biometricConfiguration +{ + return [_biometricConfiguration copy]; +} + - (PowerAuthKeychainConfiguration*) keychainConfiguration { return [_keychainConfiguration copy]; @@ -386,7 +423,7 @@ - (NSData*) biometryRelatedKeyWithAuthentication:(nonnull PowerAuthKeychainAuthe *error = nil; } - if (key && _keychainConfiguration.invalidateLocalAuthenticationContextAfterUse) { + if (key && _biometricConfiguration.invalidateLocalAuthenticationContextAfterUse) { [authentication.context invalidate]; } return key; @@ -486,31 +523,60 @@ - (PowerAuthCoreSignatureFactor) determineSignatureFactorForAuthentication:(Powe #pragma mark Initializers and SDK instance getters -static PowerAuthSDK * s_inst; - -- (nullable instancetype) initWithConfiguration:(nonnull PowerAuthConfiguration *)configuration - keychainConfiguration:(nullable PowerAuthKeychainConfiguration *)keychainConfiguration - clientConfiguration:(nullable PowerAuthClientConfiguration *)clientConfiguration +- (instancetype) initWithConfiguration:(nonnull PowerAuthConfiguration *)configuration + biometricConfiguration:(nullable PowerAuthBiometricConfiguration *)biometricConfiguration + clientConfiguration:(nullable PowerAuthClientConfiguration *)clientConfiguration + keychainConfiguration:(nullable PowerAuthKeychainConfiguration *)keychainConfiguration { self = [super init]; if (self) { [self initializeWithConfiguration:configuration - keychainConfiguration:keychainConfiguration - clientConfiguration:clientConfiguration]; + biometricConfiguration:biometricConfiguration + clientConfiguration:clientConfiguration + keychainConfiguration:keychainConfiguration]; } return self; } -- (instancetype)initWithConfiguration:(PowerAuthConfiguration *)configuration +- (instancetype) initWithConfiguration:(nonnull PowerAuthConfiguration *)configuration + biometricConfiguration:(nullable PowerAuthBiometricConfiguration *)biometricConfiguration + clientConfiguration:(nullable PowerAuthClientConfiguration *)clientConfiguration { - return [self initWithConfiguration:configuration keychainConfiguration:nil clientConfiguration:nil]; + return [self initWithConfiguration:configuration + biometricConfiguration:biometricConfiguration + clientConfiguration:clientConfiguration + keychainConfiguration:nil]; } +- (instancetype) initWithConfiguration:(PowerAuthConfiguration *)configuration +{ + return [self initWithConfiguration:configuration + biometricConfiguration:nil + clientConfiguration:nil + keychainConfiguration:nil]; +} + +// PA2_DEPRECATED(1.10.0) +- (instancetype) initWithConfiguration:(nonnull PowerAuthConfiguration *)configuration + keychainConfiguration:(nullable PowerAuthKeychainConfiguration *)keychainConfiguration + clientConfiguration:(nullable PowerAuthClientConfiguration *)clientConfiguration +{ + return [self initWithConfiguration:configuration + biometricConfiguration:nil + clientConfiguration:clientConfiguration + keychainConfiguration:keychainConfiguration]; +} + +// PA2_DEPRECATED(1.10.0) + (void) initSharedInstance:(PowerAuthConfiguration*)configuration { [self initSharedInstance:configuration keychainConfiguration:nil clientConfiguration:nil]; } +// PA2_DEPRECATED(1.10.0) +static PowerAuthSDK * s_inst; + +// PA2_DEPRECATED(1.10.0) + (void) initSharedInstance:(nonnull PowerAuthConfiguration *)configuration keychainConfiguration:(nullable PowerAuthKeychainConfiguration *)keychainConfiguration clientConfiguration:(nullable PowerAuthClientConfiguration *)clientConfiguration @@ -518,11 +584,13 @@ + (void) initSharedInstance:(nonnull PowerAuthConfiguration *)configuration static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ s_inst = [[PowerAuthSDK alloc] initWithConfiguration:configuration - keychainConfiguration:keychainConfiguration - clientConfiguration:clientConfiguration]; + biometricConfiguration:nil + clientConfiguration:clientConfiguration + keychainConfiguration:keychainConfiguration]; }); } +// PA2_DEPRECATED(1.10.0) + (PowerAuthSDK*) sharedInstance { if (!s_inst) { @@ -753,7 +821,7 @@ - (BOOL) persistActivationWithAuthentication:(PowerAuthAuthentication*)authentic if (result) { [_biometryOnlyKeychain deleteDataForKey:_biometryKeyIdentifier]; if (biometryKey) { - [_biometryOnlyKeychain addValue:biometryKey forKey:_biometryKeyIdentifier access:_keychainConfiguration.biometricItemAccess]; + [_biometryOnlyKeychain addValue:biometryKey forKey:_biometryKeyIdentifier access:_biometricConfiguration.biometricItemAccess]; } // Clear TokenStore [_tokenStore removeAllLocalTokens]; @@ -1186,7 +1254,7 @@ - (BOOL) unsafeChangePasswordFrom:(NSString*)oldPassword if ([session addBiometryFactor:encryptedEncryptionKey keys:keys]) { // Update keychain values after each successful calculations [_biometryOnlyKeychain deleteDataForKey:_biometryKeyIdentifier]; - [_biometryOnlyKeychain addValue:keys.biometryUnlockKey forKey:_biometryKeyIdentifier access:_keychainConfiguration.biometricItemAccess]; + [_biometryOnlyKeychain addValue:keys.biometryUnlockKey forKey:_biometryKeyIdentifier access:_biometricConfiguration.biometricItemAccess]; return nil; } else { return PA2MakeError(PowerAuthErrorCode_InvalidActivationState, nil); @@ -1293,7 +1361,7 @@ - (void) authenticateUsingBiometryImpl:(PowerAuthKeychainAuthentication *)keycha } // Prepare policy based on keychain configuration. LAPolicy policy; - if (_keychainConfiguration.biometricItemAccess == PowerAuthKeychainItemAccess_AnyBiometricSetOrDevicePasscode) { + if (_biometricConfiguration.biometricItemAccess == PowerAuthKeychainItemAccess_AnyBiometricSetOrDevicePasscode) { // The naming is awkward, but 'LAPolicyDeviceOwnerAuthentication' really means that // we're requesting biometry and the device's passcode policy = LAPolicyDeviceOwnerAuthentication; diff --git a/proj-xcode/PowerAuth2/private/PowerAuthSDK+Private.h b/proj-xcode/PowerAuth2/private/PowerAuthSDK+Private.h index 4159c7dd..e3335c70 100644 --- a/proj-xcode/PowerAuth2/private/PowerAuthSDK+Private.h +++ b/proj-xcode/PowerAuth2/private/PowerAuthSDK+Private.h @@ -74,12 +74,15 @@ #endif // defined(PA2_WATCH_SUPPORT) // ----------------------------------------------------------------------- -// Reveal private readonly property that helps distinguish between "current" or "any set" biometric access. -@interface PowerAuthKeychainConfiguration (BiometricAccess) -@property (nonatomic, readonly) PowerAuthKeychainItemAccess biometricItemAccess; -@end - // Reveal private property that helps convert LAContext or prompt into PowerAuthKeychainAuthentication. @interface PowerAuthAuthentication (KeychainAuth) @property (nonatomic, strong, readonly) PowerAuthKeychainAuthentication * keychainAuthentication; @end + +@interface PowerAuthBiometricConfiguration (PrivateSupport) +// Reveal private constructor that allows create PowerAuthBiometricConfiguration from PowerAuthKeychainConfiguration +// PA2_DEPRECATED(1.10.0), remove in 2.0.0 +- (instancetype) initWithKeychainConfiguration:(PowerAuthKeychainConfiguration*)keychainConfiguration; +// Reveal private readonly property that helps distinguish between "current" or "any set" biometric access. +@property (nonatomic, readonly) PowerAuthKeychainItemAccess biometricItemAccess; +@end diff --git a/proj-xcode/PowerAuth2/private/PowerAuthSDK+Private.m b/proj-xcode/PowerAuth2/private/PowerAuthSDK+Private.m index ecb35279..97a0632b 100644 --- a/proj-xcode/PowerAuth2/private/PowerAuthSDK+Private.m +++ b/proj-xcode/PowerAuth2/private/PowerAuthSDK+Private.m @@ -58,18 +58,3 @@ - (PowerAuthAuthorizationHttpHeader*) authorizationHeaderForData:(NSData*)data } @end - -@implementation PowerAuthKeychainConfiguration (BiometricAccess) - -- (PowerAuthKeychainItemAccess) biometricItemAccess -{ - if (self.allowBiometricAuthenticationFallbackToDevicePasscode) { - return PowerAuthKeychainItemAccess_AnyBiometricSetOrDevicePasscode; - } else if (self.linkBiometricItemsToCurrentSet) { - return PowerAuthKeychainItemAccess_CurrentBiometricSet; - } else { - return PowerAuthKeychainItemAccess_AnyBiometricSet; - } -} - -@end diff --git a/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSDKDefaultTests.h b/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSDKDefaultTests.h index 91cefbc3..f78d5fb2 100644 --- a/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSDKDefaultTests.h +++ b/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSDKDefaultTests.h @@ -31,6 +31,7 @@ @property (nonatomic, strong, readonly) PowerAuthSDK * sdk; - (void) prepareConfigs:(PowerAuthConfiguration**)configuration + biometricConfig:(PowerAuthBiometricConfiguration**)biometricConfiguration keychainConfig:(PowerAuthKeychainConfiguration**)keychainConfiguration clientConfig:(PowerAuthClientConfiguration**)clientConfiguration forTestName:(NSString*)testName; diff --git a/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSDKDefaultTests.m b/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSDKDefaultTests.m index f0555390..8a170d9d 100644 --- a/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSDKDefaultTests.m +++ b/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSDKDefaultTests.m @@ -35,6 +35,7 @@ - (void) tearDown - (void) prepareConfigs:(PowerAuthConfiguration**)configuration + biometricConfig:(PowerAuthBiometricConfiguration**)biometricConfiguration keychainConfig:(PowerAuthKeychainConfiguration**)keychainConfiguration clientConfig:(PowerAuthClientConfiguration**)clientConfiguration forTestName:(NSString*)testName @@ -46,8 +47,8 @@ - (void) prepareConfigs:(PowerAuthConfiguration**)configuration - (void) reconfigureForTest:(NSString *)testName { - _helper = [PowerAuthSdkTestHelper createCustom:^(PowerAuthConfiguration **configuration, PowerAuthKeychainConfiguration **keychainConfiguration, PowerAuthClientConfiguration **clientConfiguration) { - [self prepareConfigs:configuration keychainConfig:keychainConfiguration clientConfig:clientConfiguration forTestName:testName]; + _helper = [PowerAuthSdkTestHelper createCustom:^(PowerAuthConfiguration **configuration, PowerAuthBiometricConfiguration **biometricConfiguration, PowerAuthKeychainConfiguration **keychainConfiguration, PowerAuthClientConfiguration **clientConfiguration) { + [self prepareConfigs:configuration biometricConfig:biometricConfiguration keychainConfig:keychainConfiguration clientConfig:clientConfiguration forTestName:testName]; }]; [_helper printConfig]; _sdk = _helper.sdk; @@ -919,7 +920,7 @@ - (void) testEEKFromConfiguration NSData * eek = [PowerAuthCoreSession generateSignatureUnlockKey]; PowerAuthConfiguration * newConfig = [_sdk.configuration copy]; newConfig.externalEncryptionKey = eek; - _sdk = [_helper reCreateSdkInstanceWithConfiguration:newConfig keychainConfiguration:nil clientConfiguration:nil]; + _sdk = [_helper reCreateSdkInstanceWithConfiguration:newConfig biometricConfiguration:nil keychainConfiguration:nil clientConfiguration:nil]; XCTAssertTrue(_sdk.hasExternalEncryptionKey); PowerAuthSdkActivation * activation = [_helper createActivation:YES]; @@ -997,7 +998,7 @@ - (void) testSetEEKAfterActivation // Now re-instantiate SDK and try to set EEK manually PowerAuthConfiguration * newConfig = [_sdk.configuration copy]; newConfig.externalEncryptionKey = nil; - _sdk = [_helper reCreateSdkInstanceWithConfiguration:newConfig keychainConfiguration:nil clientConfiguration:nil]; + _sdk = [_helper reCreateSdkInstanceWithConfiguration:newConfig biometricConfiguration:nil keychainConfiguration:nil clientConfiguration:nil]; XCTAssertFalse(_sdk.hasExternalEncryptionKey); // Activation status should work PowerAuthActivationStatus * status = [_helper fetchActivationStatus]; @@ -1274,7 +1275,7 @@ - (void) testEncryptorCreation } // Re-create SDK to reset internal objects - _sdk = [_helper reCreateSdkInstanceWithConfiguration:nil keychainConfiguration:nil clientConfiguration:nil]; + _sdk = [_helper reCreateSdkInstanceWithConfiguration:nil biometricConfiguration:nil keychainConfiguration:nil clientConfiguration:nil]; XCTAssertTrue(_sdk.hasValidActivation); encryptor = [AsyncHelper synchronizeAsynchronousBlock:^(AsyncHelper *waiting) { diff --git a/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSDKSharedTests.m b/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSDKSharedTests.m index 7f88c50c..e1e752e1 100644 --- a/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSDKSharedTests.m +++ b/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSDKSharedTests.m @@ -62,6 +62,7 @@ - (void) tearDown } - (void) prepareConfigs:(PowerAuthConfiguration **)configuration + biometricConfig:(PowerAuthBiometricConfiguration **)biometricConfiguration keychainConfig:(PowerAuthKeychainConfiguration **)keychainConfiguration clientConfig:(PowerAuthClientConfiguration **)clientConfiguration forTestName:(NSString*)testName @@ -76,6 +77,7 @@ - (void) prepareConfigs:(PowerAuthConfiguration **)configuration (*configuration).sharingConfiguration = sharingConfig; [super prepareConfigs:configuration + biometricConfig:biometricConfiguration keychainConfig:keychainConfiguration clientConfig:clientConfiguration forTestName:testName]; diff --git a/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSdkTestHelper.h b/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSdkTestHelper.h index bd3e8fc8..ea04fe72 100644 --- a/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSdkTestHelper.h +++ b/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSdkTestHelper.h @@ -58,7 +58,7 @@ typedef NS_OPTIONS(NSUInteger, TestActivationFlags) { /** Create custom helper that allows you alter configurations provided to PowerAuthSDK before the instance is created. */ -+ (PowerAuthSdkTestHelper*) createCustom:(void (^)(PowerAuthConfiguration ** configuration, PowerAuthKeychainConfiguration ** keychainConfiguration, PowerAuthClientConfiguration ** clientConfiguration))configurator; ++ (PowerAuthSdkTestHelper*) createCustom:(void (^)(PowerAuthConfiguration ** configuration, PowerAuthBiometricConfiguration ** biometricConfiguration, PowerAuthKeychainConfiguration ** keychainConfiguration, PowerAuthClientConfiguration ** clientConfiguration))configurator; /** Clone the existing test helper but with altered configuration. */ @@ -85,6 +85,7 @@ typedef NS_OPTIONS(NSUInteger, TestActivationFlags) { appropriate configuration from current PowerAuthSDK will be used. */ - (PowerAuthSDK*) reCreateSdkInstanceWithConfiguration:(PowerAuthConfiguration*)configuration + biometricConfiguration:(PowerAuthBiometricConfiguration*)biometricConfiguration keychainConfiguration:(PowerAuthKeychainConfiguration*)keychainConfiguration clientConfiguration:(PowerAuthClientConfiguration*)clientConfiguration; diff --git a/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSdkTestHelper.m b/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSdkTestHelper.m index 4d11f208..e6d1630f 100644 --- a/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSdkTestHelper.m +++ b/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSdkTestHelper.m @@ -106,7 +106,7 @@ + (PowerAuthSdkTestHelper*) createDefault return [self createCustom:nil]; } -+ (PowerAuthSdkTestHelper*) createCustom:(void (^)(PowerAuthConfiguration ** configuration, PowerAuthKeychainConfiguration ** keychainConfiguration, PowerAuthClientConfiguration ** clientConfiguration))configurator ++ (PowerAuthSdkTestHelper*) createCustom:(void (^)(PowerAuthConfiguration ** configuration, PowerAuthBiometricConfiguration ** biometricConfiguration, PowerAuthKeychainConfiguration ** keychainConfiguration, PowerAuthClientConfiguration ** clientConfiguration))configurator { [self setupLog]; @@ -135,10 +135,11 @@ + (PowerAuthSdkTestHelper*) createCustom:(void (^)(PowerAuthConfiguration ** con config.appSecret = testServerApi.appVersion.applicationSecret; config.masterServerPublicKey = testServerApi.appDetail.masterPublicKey; #endif // PA2_SIMPLIFIED_CONFIG - PowerAuthKeychainConfiguration * keychainConfig = [[PowerAuthKeychainConfiguration sharedInstance] copy]; - PowerAuthClientConfiguration * clientConfig = [[PowerAuthClientConfiguration sharedInstance] copy]; + PowerAuthKeychainConfiguration * keychainConfig = [[PowerAuthKeychainConfiguration alloc] init]; + PowerAuthClientConfiguration * clientConfig = [[PowerAuthClientConfiguration alloc] init]; + PowerAuthBiometricConfiguration * biometricConfig = [[PowerAuthBiometricConfiguration alloc] init]; if (configurator) { - configurator(&config, &keychainConfig, &clientConfig); + configurator(&config, &biometricConfig, &keychainConfig, &clientConfig); } result = [config validateConfiguration]; XCTAssertTrue(result, @"Constructed configuration is not valid."); @@ -147,8 +148,9 @@ + (PowerAuthSdkTestHelper*) createCustom:(void (^)(PowerAuthConfiguration ** con } PowerAuthSDK *sdk = [[PowerAuthSDK alloc] initWithConfiguration:config - keychainConfiguration:keychainConfig - clientConfiguration:clientConfig]; + biometricConfiguration:biometricConfig + clientConfiguration:clientConfig + keychainConfiguration:keychainConfig]; [sdk removeActivationLocal]; result = sdk != nil; @@ -448,12 +450,16 @@ - (void) cleanup } - (PowerAuthSDK*) reCreateSdkInstanceWithConfiguration:(PowerAuthConfiguration*)configuration - keychainConfiguration:(PowerAuthKeychainConfiguration*)keychainConfiguration - clientConfiguration:(PowerAuthClientConfiguration*)clientConfiguration + biometricConfiguration:(PowerAuthBiometricConfiguration*)biometricConfiguration + keychainConfiguration:(PowerAuthKeychainConfiguration*)keychainConfiguration + clientConfiguration:(PowerAuthClientConfiguration*)clientConfiguration { if (configuration == nil) { configuration = [_sdk.configuration copy]; } + if (biometricConfiguration == nil) { + biometricConfiguration = [_sdk.biometricConfiguration copy]; + } if (keychainConfiguration == nil) { keychainConfiguration = [_sdk.keychainConfiguration copy]; } @@ -461,8 +467,9 @@ - (PowerAuthSDK*) reCreateSdkInstanceWithConfiguration:(PowerAuthConfiguration*) clientConfiguration = [_sdk.clientConfiguration copy]; } _sdk = [[PowerAuthSDK alloc] initWithConfiguration:configuration - keychainConfiguration:keychainConfiguration - clientConfiguration:clientConfiguration]; + biometricConfiguration:biometricConfiguration + clientConfiguration:clientConfiguration + keychainConfiguration:keychainConfiguration]; return _sdk; } diff --git a/proj-xcode/PowerAuth2IntegrationTests/PowerAuthTokenTests.m b/proj-xcode/PowerAuth2IntegrationTests/PowerAuthTokenTests.m index 936f912f..6f0598b2 100644 --- a/proj-xcode/PowerAuth2IntegrationTests/PowerAuthTokenTests.m +++ b/proj-xcode/PowerAuth2IntegrationTests/PowerAuthTokenTests.m @@ -114,7 +114,10 @@ - (void) testBasicTokenOperations XCTAssertTrue(result); // Simulate application's restart - _sdk = [_helper reCreateSdkInstanceWithConfiguration:_sdk.configuration keychainConfiguration:_sdk.keychainConfiguration clientConfiguration:_sdk.clientConfiguration]; + _sdk = [_helper reCreateSdkInstanceWithConfiguration:_sdk.configuration + biometricConfiguration:_sdk.biometricConfiguration + keychainConfiguration:_sdk.keychainConfiguration + clientConfiguration:_sdk.clientConfiguration]; tokenStore = _sdk.tokenStore; // Calculate header with asynchronous method From 0265d0f8cd1e8e1fc03b2b11a3d68f9a70c4df6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraj=20=C4=8Eurech?= Date: Thu, 23 Jan 2025 10:49:53 +0100 Subject: [PATCH 2/2] Android: Added `PowerAuthBiometricConfiguration` class #658 - Deprecated PowerAuthKeychainConfiguration properties moved to new biometric configuration --- docs/Migration-from-1.9-to-1.10.md | 10 + docs/PowerAuth-SDK-for-Android.md | 56 +++-- ...AuthBiometricConfigurationBuilderTest.java | 74 +++++++ ...rAuthKeychainConfigurationBuilderTest.java | 1 + .../sdk/PowerAuthBiometricConfiguration.java | 209 ++++++++++++++++++ .../sdk/PowerAuthKeychainConfiguration.java | 24 ++ .../security/powerauth/sdk/PowerAuthSDK.java | 51 ++++- 7 files changed, 401 insertions(+), 24 deletions(-) create mode 100644 proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/sdk/PowerAuthBiometricConfigurationBuilderTest.java create mode 100644 proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthBiometricConfiguration.java diff --git a/docs/Migration-from-1.9-to-1.10.md b/docs/Migration-from-1.9-to-1.10.md index 17574e82..e7bb4adb 100644 --- a/docs/Migration-from-1.9-to-1.10.md +++ b/docs/Migration-from-1.9-to-1.10.md @@ -16,6 +16,16 @@ PowerAuth Mobile SDK in version `1.10.0` provides the following improvements: - The following methods in `PowerAuthSDK` class are deprecated: - `changePasswordUnsafe()` - use asynchronous `changePassword()` as a replacement. +- The following methods in `PowerAuthKeychainConfiguration` are now deprecated: + - `isLinkBiometricItemsToCurrentSet()` - use `PowerAuthBiometricConfiguration.isInvalidateBiometricFactorAfterChange()` instead. + - `isConfirmBiometricAuthentication()` - use equal method in `PowerAuthBiometricConfiguration` instead. + - `isAuthenticateOnBiometricKeySetup()` - use equal method in `PowerAuthBiometricConfiguration` instead. + - `isFallbackToSharedBiometryKeyEnabled()` - use equal method in `PowerAuthBiometricConfiguration` instead. + - `Builder.linkBiometricItemsToCurrentSet()` - use `PowerAuthBiometricConfiguration.Builder.invalidateBiometricFactorAfterChange(boolean)` instead. + - `Builder.confirmBiometricAuthentication()` - use equal method in `PowerAuthBiometricConfiguration.Builder` instead. + - `Builder.authenticateOnBiometricKeySetup()` - use equal method in `PowerAuthBiometricConfiguration.Builder` instead. + - `Builder.enableFallbackToSharedBiometryKey()` - use equal method in `PowerAuthBiometricConfiguration.Builder` instead. + - Due to removed support of recovery codes, the following classes and methods are no longer available: - Methods removed in `PowerAuthSDK`: - `createRecoveryActivation()` diff --git a/docs/PowerAuth-SDK-for-Android.md b/docs/PowerAuth-SDK-for-Android.md index a8b317ce..ce1d2993 100644 --- a/docs/PowerAuth-SDK-for-Android.md +++ b/docs/PowerAuth-SDK-for-Android.md @@ -148,6 +148,16 @@ The `PowerAuthConfiguration.Builder` class provides the following additional met - `externalEncryptionKey()` - See [External Encryption Key](#external-encryption-key) chapter for more details. - `disableAutomaticProtocolUpgrade()` - Disables the automatic protocol upgrade. This option should be used only for debugging purposes. +### Biometric configuration + +The `PowerAuthBiometricConfiguration.Builder` class contains biometric configuration for `PowerAuthSDK` class. It has the following configuration properties: + +- `invalidateBiometricFactorAfterChange()` - Function specifies whether the biometric factor key is invalidated if fingers are added or removed, or if the user re-enrolls for face. See [Biometry Factor-Related Key Lifetime](#biometry-factor-related-key-lifetime) chapter for more details. +- `confirmBiometricAuthentication()` - Function specifies whether the user's confirmation will be required after the successful biometric authentication. See [Biometric Authentication Confirmation](#biometric-authentication-confirmation) chapter for more details. +- `authenticateOnBiometricKeySetup()` - Function specifies whether setup for biometric factor always require a biometric authentication. See [Enable Biometric Authentication](#enable-biometric-authentication) chapter for more details. +- `enableFallbackToSharedBiometryKey()` - Function specifies whether `PowerAuthSDK` instance should also do additional lookup for a legacy biometric key, previously shared between multiple `PowerAuthSDK` object instances. The default value is `true` and the fallback is enabled. If your application is using multiple `PowerAuthSDK` instances, then it's recommended to set this option to `false` to avoid use of the shared key between such instances. + + ### HTTP client configuration The `PowerAuthClientConfiguration.Builder` class contains configuration for a HTTP client used internally by `PowerAuthSDK` class. It has the following configuration properties: @@ -162,10 +172,6 @@ The `PowerAuthClientConfiguration.Builder` class contains configuration for a HT The `PowerAuthKeychainConfiguration.Builder` class contains configuration for a keychain-based storage used by `PowerAuthSDK` class internally. The configuration contains the following properties: -- `linkBiometricItemsToCurrentSet()` - Function specifies whether the item protected with the biometry is invalidated if fingers are added or removed, or if the user re-enrolls for face. See [Biometry Factor-Related Key Lifetime](#biometry-factor-related-key-lifetime) chapter for more details. -- `confirmBiometricAuthentication()` - Function specifies whether the user's confirmation will be required after the successful biometric authentication. See [Biometric Authentication Confirmation](#biometric-authentication-confirmation) chapter for more details. -- `authenticateOnBiometricKeySetup()` - Function specifies whether biometric key setup always require a biometric authentication. See [Enable Biometric Authentication](#enable-biometric-authentication) chapter for more details. -- `enableFallbackToSharedBiometryKey()` - Function specifies whether `PowerAuthSDK` instance should also do additional lookup for a legacy biometric key, previously shared between multiple PowerAuthSDK object instances. The default value is `true` and the fallback is enabled. If your application is using multiple `PowerAuthSDK` instances, then it's recommended to set this option to `false` to avoid use of the shared key between such instances. - `minimalRequiredKeychainProtection()` - Function specifies minimal required keychain protection level that must be supported on the current device. See [Activation Data Protection](#activation-data-protection) chapter for more details. The following properties are also available for configuration but are not recommended to be altered under typical circumstances, as changing them may impact the library’s stability or intended behavior: @@ -175,6 +181,12 @@ The following properties are also available for configuration but are not recomm - `keychainTokenStoreId()` - Function specifies name of the Keychain file used for storing the access tokens. - `keychainKeyBiometry()` - Function specifies name of the key to the biometry Keychain to store biometry-factor protection key. +The following properties are deprecated and moved to [Biometric Configuration](#biometric-configuration) class: + +- `linkBiometricItemsToCurrentSet()` - Function specifies whether the item protected with the biometry is invalidated if fingers are added or removed, or if the user re-enrolls for face. See [Biometry Factor-Related Key Lifetime](#biometry-factor-related-key-lifetime) chapter for more details. +- `confirmBiometricAuthentication()` - Function specifies whether the user's confirmation will be required after the successful biometric authentication. See [Biometric Authentication Confirmation](#biometric-authentication-confirmation) chapter for more details. +- `authenticateOnBiometricKeySetup()` - Function specifies whether biometric key setup always require a biometric authentication. See [Enable Biometric Authentication](#enable-biometric-authentication) chapter for more details. +- `enableFallbackToSharedBiometryKey()` - Function specifies whether `PowerAuthSDK` instance should also do additional lookup for a legacy biometric key, previously shared between multiple PowerAuthSDK object instances. The default value is `true` and the fallback is enabled. If your application is using multiple `PowerAuthSDK` instances, then it's recommended to set this option to `false` to avoid use of the shared key between such instances. ### Activation Data Protection @@ -1620,25 +1632,25 @@ powerAuthSDK.addBiometryFactor(context, fragment, "Enable Biometric Authenticati ``` -By default, PowerAuth SDK asks the user to authenticate with the biometric sensor also during the setup procedure (or during the [activation persist](#persisting-activation-data)). To alter this behavior, use the following code to change the `PowerAuthKeychainConfiguration` provided to the `PowerAuthSDK` instance: +By default, PowerAuth SDK asks the user to authenticate with the biometric sensor also during the setup procedure (or during the [activation persist](#persisting-activation-data)). To alter this behavior, use the following code to change the `PowerAuthBiometricConfiguration` provided to the `PowerAuthSDK` instance: ```kotlin -val keychainConfig = PowerAuthKeychainConfiguration.Builder() +val biometricConfig = PowerAuthBiometricConfiguration.Builder() .authenticateOnBiometricKeySetup(false) .build() // Apply keychain configuration val powerAuthSDK = PowerAuthSDK.Builder(configuration) - .keychainConfiguration(keychainConfig) + .biometricConfiguration(biometricConfig) .build(getApplicationContext()) ``` ```java -PowerAuthKeychainConfiguration keychainConfig = new PowerAuthKeychainConfiguration.Builder() +PowerAuthBiometricConfiguration biometricConfig = new PowerAuthBiometricConfiguration.Builder() .authenticateOnBiometricKeySetup(false) .build(); // Apply keychain configuration PowerAuthSDK powerAuthSDK = new PowerAuthSDK.Builder(configuration) - .keychainConfiguration(keychainConfig) + .biometricConfiguration(biometricConfig) .build(getApplicationContext()); ``` @@ -1745,27 +1757,27 @@ The Android source codes contain a list of devices with strong biometry support. ### Biometry Factor-Related Key Lifetime -By default, the biometry factor-related key is invalidated after the biometry enrolled in the system is changed. For example, if the user adds or removes the finger or enrolls with a new face, then the biometry factor-related key is no longer available for the signing operation. To change this behavior, you have to provide the `PowerAuthKeychainConfiguration` object with the `linkBiometricItemsToCurrentSet` parameter set to `false` and use that configuration for the `PowerAuthSDK` instance construction: +By default, the biometry factor-related key is invalidated after the biometry enrolled in the system is changed. For example, if the user adds or removes the finger or enrolls with a new face, then the biometry factor-related key is no longer available for the signing operation. To change this behavior, you have to provide the `PowerAuthBiometricConfiguration` object with the `invalidateBiometricFactorAfterChange` parameter set to `false` and use that configuration for the `PowerAuthSDK` instance construction: ```kotlin -// Use false for the 'linkBiometricItemsToCurrentSet' parameter. -val keychainConfig = PowerAuthKeychainConfiguration.Builder() - .linkBiometricItemsToCurrentSet(false) +// Use false for the 'invalidateBiometricFactorAfterChange' parameter. +val biometricConfig = PowerAuthBiometricConfiguration.Builder() + .invalidateBiometricFactorAfterChange(false) .build() // Apply keychain configuration val powerAuthSDK = PowerAuthSDK.Builder(configuration) - .keychainConfiguration(keychainConfig) + .biometricConfiguration(biometricConfig) .build(getApplicationContext()) ``` ```java // Use false for the 'linkBiometricItemsToCurrentSet' parameter. -PowerAuthKeychainConfiguration keychainConfig = new PowerAuthKeychainConfiguration.Builder() - .linkBiometricItemsToCurrentSet(false) +PowerAuthBiometricConfiguration biometricConfig = new PowerAuthBiometricConfiguration.Builder() + .invalidateBiometricFactorAfterChange(false) .build(); // Apply keychain configuration PowerAuthSDK powerAuthSDK = new PowerAuthSDK.Builder(configuration) - .keychainConfiguration(keychainConfig) + .biometricConfiguration(biometricConfig) .build(getApplicationContext()); ``` @@ -1850,27 +1862,27 @@ Note that you still should [Customize Biometric Dialog Resources](#customize-bio #### 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 the `PowerAuthKeychainConfiguration` object with the `confirmBiometricAuthentication` parameter set to `true` and use that configuration for the `PowerAuthSDK` instance construction: +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 the `PowerAuthBiometricConfiguration` object with the `confirmBiometricAuthentication` parameter set to `true` and use that configuration for the `PowerAuthSDK` instance construction: ```kotlin // Use true for the 'confirmBiometricAuthentication' parameter. -val keychainConfig = PowerAuthKeychainConfiguration.Builder() +val biometricConfig = PowerAuthBiometricConfiguration.Builder() .confirmBiometricAuthentication(true) .build() // Apply keychain configuration val powerAuthSDK = PowerAuthSDK.Builder(configuration) - .keychainConfiguration(keychainConfig) + .biometricConfiguration(biometricConfig) .build(context) ``` ```java // Use true for the 'confirmBiometricAuthentication' parameter. -PowerAuthKeychainConfiguration keychainConfig = new PowerAuthKeychainConfiguration.Builder() +PowerAuthBiometricConfiguration biometricConfig = new PowerAuthBiometricConfiguration.Builder() .confirmBiometricAuthentication(true) .build(); // Apply keychain configuration PowerAuthSDK powerAuthSDK = new PowerAuthSDK.Builder(configuration) - .keychainConfiguration(keychainConfig) + .biometricConfiguration(biometricConfig) .build(getApplicationContext()); ``` diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/sdk/PowerAuthBiometricConfigurationBuilderTest.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/sdk/PowerAuthBiometricConfigurationBuilderTest.java new file mode 100644 index 00000000..3db7ac2d --- /dev/null +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/sdk/PowerAuthBiometricConfigurationBuilderTest.java @@ -0,0 +1,74 @@ +/* + * Copyright 2025 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.sdk; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; +import static org.junit.Assert.assertTrue; + +@RunWith(AndroidJUnit4.class) +public class PowerAuthBiometricConfigurationBuilderTest { + + @Test + public void testDefaultParameters() { + PowerAuthBiometricConfiguration configuration = new PowerAuthBiometricConfiguration.Builder() + .build(); + assertFalse(configuration.isConfirmBiometricAuthentication()); + assertTrue(configuration.isInvalidateBiometricFactorAfterChange()); + assertTrue(configuration.isAuthenticateOnBiometricKeySetup()); + assertTrue(configuration.isFallbackToSharedBiometryKeyEnabled()); + } + + @Test + public void testCustomParameters() { + for (int i = 1; i <= 4; i++) { + final boolean isConfirmBiometricAuthentication = i == 1; + final boolean isInvalidateBiometricFactorAfterChange = i == 2; + final boolean isAuthenticateOnBiometricKeySetup = i == 3; + final boolean isFallbackToSharedBiometryKeyEnabled = i == 4; + PowerAuthBiometricConfiguration configuration = new PowerAuthBiometricConfiguration.Builder() + .confirmBiometricAuthentication(isConfirmBiometricAuthentication) + .invalidateBiometricFactorAfterChange(isInvalidateBiometricFactorAfterChange) + .authenticateOnBiometricKeySetup(isAuthenticateOnBiometricKeySetup) + .enableFallbackToSharedBiometryKey(isFallbackToSharedBiometryKeyEnabled) + .build(); + assertEquals(isConfirmBiometricAuthentication, configuration.isConfirmBiometricAuthentication()); + assertEquals(isInvalidateBiometricFactorAfterChange, configuration.isInvalidateBiometricFactorAfterChange()); + assertEquals(isAuthenticateOnBiometricKeySetup, configuration.isAuthenticateOnBiometricKeySetup()); + assertEquals(isFallbackToSharedBiometryKeyEnabled, configuration.isFallbackToSharedBiometryKeyEnabled()); + } + for (int i = 1; i <= 4; i++) { + final boolean isConfirmBiometricAuthentication = i != 1; + final boolean isInvalidateBiometricFactorAfterChange = i != 2; + final boolean isAuthenticateOnBiometricKeySetup = i != 3; + final boolean isFallbackToSharedBiometryKeyEnabled = i != 4; + PowerAuthBiometricConfiguration configuration = new PowerAuthBiometricConfiguration.Builder() + .confirmBiometricAuthentication(isConfirmBiometricAuthentication) + .invalidateBiometricFactorAfterChange(isInvalidateBiometricFactorAfterChange) + .authenticateOnBiometricKeySetup(isAuthenticateOnBiometricKeySetup) + .enableFallbackToSharedBiometryKey(isFallbackToSharedBiometryKeyEnabled) + .build(); + assertEquals(isConfirmBiometricAuthentication, configuration.isConfirmBiometricAuthentication()); + assertEquals(isInvalidateBiometricFactorAfterChange, configuration.isInvalidateBiometricFactorAfterChange()); + assertEquals(isAuthenticateOnBiometricKeySetup, configuration.isAuthenticateOnBiometricKeySetup()); + assertEquals(isFallbackToSharedBiometryKeyEnabled, configuration.isFallbackToSharedBiometryKeyEnabled()); + } + } +} diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/sdk/PowerAuthKeychainConfigurationBuilderTest.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/sdk/PowerAuthKeychainConfigurationBuilderTest.java index 2554b6f2..cd8cb26f 100644 --- a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/sdk/PowerAuthKeychainConfigurationBuilderTest.java +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/sdk/PowerAuthKeychainConfigurationBuilderTest.java @@ -25,6 +25,7 @@ import static org.junit.Assert.*; +/** @noinspection deprecation*/ @RunWith(AndroidJUnit4.class) public class PowerAuthKeychainConfigurationBuilderTest { diff --git a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthBiometricConfiguration.java b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthBiometricConfiguration.java new file mode 100644 index 00000000..e1110cc7 --- /dev/null +++ b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthBiometricConfiguration.java @@ -0,0 +1,209 @@ +/* + * Copyright 2025 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.sdk; + +import androidx.annotation.NonNull; + +/** + * Class representing the biometric settings for {@link PowerAuthSDK} class. + */ +public class PowerAuthBiometricConfiguration { + + private final boolean invalidateBiometricFactorAfterChange; + private final boolean confirmBiometricAuthentication; + private final boolean authenticateOnBiometricKeySetup; + private final boolean enableFallbackToSharedBiometryKey; + + /** + * Get information whether the biometric factor is invalidated when the biometric configuration changes + * in the system. + *

+ * If set, then the biometric factor key in {@code PowerAuthSDK} instance is invalidated if fingers are added or removed, + * or if the user re-enrolls for face. The default value is {@code true} (e.g. changing biometry + * in the system invalidate the entry) + * + * @return {@code true} when items protected with biometry are linked to the current set + * of biometry, configured in the system. + */ + public boolean isInvalidateBiometricFactorAfterChange() { + return invalidateBiometricFactorAfterChange; + } + + /** + * Get information whether additional user's confirmation should be required after the successful + * biometric authentication. + * + * @return {@code true} if additional user's confirmation should be required after the successful + * biometric authentication. + */ + public boolean isConfirmBiometricAuthentication() { + return confirmBiometricAuthentication; + } + + /** + * Get whether biometric authentication is required also for biometric key setup. + * + * @return {@code true} if biometric authentication is required for biometric key setup. + */ + public boolean isAuthenticateOnBiometricKeySetup() { + return authenticateOnBiometricKeySetup; + } + + /** + * Get whether fallback to shared, legacy biometry key is enabled. By default, this is enabled for the compatibility + * reasons. If set, then {@code PowerAuthSDK} does additional lookup for a legacy biometric key, previously shared + * between multiple {@code PowerAuthSDK} object instances. + * + * @return {@code true} if fallback to shared, legacy biometry key is enabled. + */ + public boolean isFallbackToSharedBiometryKeyEnabled() { + return enableFallbackToSharedBiometryKey; + } + + /** + * Private constructor. Use {@link Builder} to create a new instance of this class. + * + * @param invalidateBiometricFactorAfterChange If set, then the biometric factor is invalidated + * if fingers are added or removed, or if the user re-enrolls for face. + * @param confirmBiometricAuthentication If set, then the user's confirmation will be required after the successful + * biometric authentication. Note that this is just hint for the system + * and may be ignored. + * @param authenticateOnBiometricKeySetup If set, then the biometric key setup always require biometric authentication. + * If not set, then only usage of biometric key require biometric authentication. + * @param enableFallbackToSharedBiometryKey If set, then the PowerAuthSDK does one more additional lookup to use legacy + * key shared between multiple PowerAuthSDK instances. + */ + private PowerAuthBiometricConfiguration( + boolean invalidateBiometricFactorAfterChange, + boolean confirmBiometricAuthentication, + boolean authenticateOnBiometricKeySetup, + boolean enableFallbackToSharedBiometryKey) { + this.invalidateBiometricFactorAfterChange = invalidateBiometricFactorAfterChange; + this.confirmBiometricAuthentication = confirmBiometricAuthentication; + this.authenticateOnBiometricKeySetup = authenticateOnBiometricKeySetup; + this.enableFallbackToSharedBiometryKey = enableFallbackToSharedBiometryKey; + } + + /** + * Internal constructor that create the biometric configuration from provided keychain configuration. + * @noinspection deprecation + */ + // @Deprecated // 1.10.0 + PowerAuthBiometricConfiguration(@NonNull PowerAuthKeychainConfiguration keychainConfiguration) { + this.invalidateBiometricFactorAfterChange = keychainConfiguration.isLinkBiometricItemsToCurrentSet(); + this.confirmBiometricAuthentication = keychainConfiguration.isConfirmBiometricAuthentication(); + this.authenticateOnBiometricKeySetup = keychainConfiguration.isAuthenticateOnBiometricKeySetup(); + this.enableFallbackToSharedBiometryKey = keychainConfiguration.isFallbackToSharedBiometryKeyEnabled(); + } + + public static final boolean DEFAULT_INVALIDATE_BIOMETRIC_FACTOR_AFTER_CHANGE = true; + public static final boolean DEFAULT_CONFIRM_BIOMETRIC_AUTHENTICATION = false; + public static final boolean DEFAULT_AUTHENTICATE_ON_BIOMETRIC_KEY_SETUP = true; + public static final boolean DEFAULT_ENABLE_FALLBACK_TO_SHARED_BIOMETRY_KEY = true; + + /** + * A builder that collects arguments for {@link PowerAuthBiometricConfiguration}. + */ + public static class Builder { + + private boolean invalidateBiometricFactorAfterChange = DEFAULT_INVALIDATE_BIOMETRIC_FACTOR_AFTER_CHANGE; + private boolean confirmBiometricAuthentication = DEFAULT_CONFIRM_BIOMETRIC_AUTHENTICATION; + private boolean authenticateOnBiometricKeySetup = DEFAULT_AUTHENTICATE_ON_BIOMETRIC_KEY_SETUP; + private boolean enableFallbackToSharedBiometryKey = DEFAULT_ENABLE_FALLBACK_TO_SHARED_BIOMETRY_KEY; + + /** + * Creates a builder for {@link PowerAuthBiometricConfiguration}. + */ + public Builder() { + } + + /** + * Set whether the biometric factor should be invalidated if fingers are added or removed, or if the user + * re-enrolls for face. + * + * @param invalidateBiometricFactorAfterChange If set, then the biometric factor is invalidated if fingers are + * added or removed, or if the user re-enrolls for face. + * @return {@link PowerAuthBiometricConfiguration.Builder} + */ + public @NonNull PowerAuthBiometricConfiguration.Builder invalidateBiometricFactorAfterChange(boolean invalidateBiometricFactorAfterChange) { + this.invalidateBiometricFactorAfterChange = invalidateBiometricFactorAfterChange; + return this; + } + + /** + * Set whether the user's confirmation will be required after the successful biometric authentication. + * + * @param confirmBiometricAuthentication If set, then the user's confirmation will be required after the successful + * biometric authentication. Note that this is just hint for the system + * and may be ignored. + * @return {@link PowerAuthBiometricConfiguration.Builder} + */ + public @NonNull PowerAuthBiometricConfiguration.Builder confirmBiometricAuthentication(boolean confirmBiometricAuthentication) { + this.confirmBiometricAuthentication = confirmBiometricAuthentication; + return this; + } + + /** + * (Optional) Set, whether biometric key setup always require a biometric authentication. + *

+ * Setting parameter to {@code true} leads to use symmetric AES cipher on the background, + * so both configuration and usage of biometric key require the biometric authentication. + *

+ * If set to {@code false}, then RSA cipher is used and only the usage of biometric key + * require the biometric authentication. This is due to fact, that RSA cipher can encrypt + * data with using its public key available immediate after the key-pair is created in + * Android KeyStore. + *

+ * The default value is {@code true}. + * + * @param authenticate If set, then biometric authentication is required for both setup and usage + * of biometric key. + * @return {@link PowerAuthBiometricConfiguration.Builder} + */ + public @NonNull PowerAuthBiometricConfiguration.Builder authenticateOnBiometricKeySetup(boolean authenticate) { + this.authenticateOnBiometricKeySetup = authenticate; + return this; + } + + /** + * (Optional) Set, whether PowerAuthSDK instance should also do additional lookup for a legacy biometric key, + * previously shared between multiple PowerAuthSDK object instances. + *

+ * The default value is {@code true} and the fallback is enabled. + * + * @param enable If {@code true} then fallback to legacy key is enabled. + * @return {@link PowerAuthBiometricConfiguration.Builder} + */ + public @NonNull PowerAuthBiometricConfiguration.Builder enableFallbackToSharedBiometryKey(boolean enable) { + this.enableFallbackToSharedBiometryKey = enable; + return this; + } + + /** + * Build final {@link PowerAuthBiometricConfiguration} object. + * + * @return New instance of {@link PowerAuthBiometricConfiguration}. + */ + public @NonNull PowerAuthBiometricConfiguration build() { + return new PowerAuthBiometricConfiguration( + invalidateBiometricFactorAfterChange, + confirmBiometricAuthentication, + authenticateOnBiometricKeySetup, + enableFallbackToSharedBiometryKey); + } + } +} diff --git a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthKeychainConfiguration.java b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthKeychainConfiguration.java index c6eff790..2b99a1f8 100644 --- a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthKeychainConfiguration.java +++ b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthKeychainConfiguration.java @@ -100,7 +100,10 @@ public class PowerAuthKeychainConfiguration { * * @return {@code true} when items protected with biometry are linked to the current set * of biometry, configured in the system. + * + * @deprecated Use {@link PowerAuthBiometricConfiguration#isInvalidateBiometricFactorAfterChange()} instead. */ + @Deprecated // 1.10.0 public boolean isLinkBiometricItemsToCurrentSet() { return linkBiometricItemsToCurrentSet; } @@ -111,7 +114,10 @@ public boolean isLinkBiometricItemsToCurrentSet() { * * @return {@code true} if additional user's confirmation should be required after the successful * biometric authentication. + * + * @deprecated Use {@link PowerAuthBiometricConfiguration#isConfirmBiometricAuthentication()} instead. */ + @Deprecated // 1.10.0 public boolean isConfirmBiometricAuthentication() { return confirmBiometricAuthentication; } @@ -120,7 +126,10 @@ public boolean isConfirmBiometricAuthentication() { * Get whether biometric authentication is required also for biometric key setup. * * @return {@code true} if biometric authentication is required for biometric key setup. + * + * @deprecated Use {@link PowerAuthBiometricConfiguration#isAuthenticateOnBiometricKeySetup()} instead. */ + @Deprecated // 1.10.0 public boolean isAuthenticateOnBiometricKeySetup() { return authenticateOnBiometricKeySetup; } @@ -131,7 +140,10 @@ public boolean isAuthenticateOnBiometricKeySetup() { * between multiple {@code PowerAuthSDK} object instances. * * @return {@code true} if fallback to shared, legacy biometry key is enabled. + * + * @deprecated Use {@link PowerAuthBiometricConfiguration#isFallbackToSharedBiometryKeyEnabled()} instead. */ + @Deprecated // 1.10.0 public boolean isFallbackToSharedBiometryKeyEnabled() { return enableFallbackToSharedBiometryKey; } @@ -273,7 +285,10 @@ public Builder() { * @param linkBiometricItemsToCurrentSet If set, then the item protected with the biometry is invalidated * if fingers are added or removed, or if the user re-enrolls for face. * @return {@link Builder} + * + * @deprecated Use {@link PowerAuthBiometricConfiguration.Builder#invalidateBiometricFactorAfterChange(boolean)} instead. */ + @Deprecated // 1.10.0 public @NonNull Builder linkBiometricItemsToCurrentSet(boolean linkBiometricItemsToCurrentSet) { this.linkBiometricItemsToCurrentSet = linkBiometricItemsToCurrentSet; return this; @@ -286,7 +301,10 @@ public Builder() { * biometric authentication. Note that this is just hint for the system * and may be ignored. * @return {@link Builder} + * + * @deprecated Use {@link PowerAuthBiometricConfiguration.Builder#confirmBiometricAuthentication(boolean)} instead. */ + @Deprecated // 1.10.0 public @NonNull Builder confirmBiometricAuthentication(boolean confirmBiometricAuthentication) { this.confirmBiometricAuthentication = confirmBiometricAuthentication; return this; @@ -308,7 +326,10 @@ public Builder() { * @param authenticate If set, then biometric authentication is required for both setup and usage * of biometric key. * @return {@link Builder} + * + * @deprecated Use {@link PowerAuthBiometricConfiguration.Builder#authenticateOnBiometricKeySetup(boolean)} instead. */ + @Deprecated // 1.10.0 public @NonNull Builder authenticateOnBiometricKeySetup(boolean authenticate) { this.authenticateOnBiometricKeySetup = authenticate; return this; @@ -322,7 +343,10 @@ public Builder() { * * @param enable If {@code true} then fallback to legacy key is enabled. * @return {@link Builder} + * + * @deprecated Use {@link PowerAuthBiometricConfiguration.Builder#enableFallbackToSharedBiometryKey(boolean)} instead. */ + @Deprecated // 1.10.0 public @NonNull Builder enableFallbackToSharedBiometryKey(boolean enable) { this.enableFallbackToSharedBiometryKey = enable; return this; 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 4bb0d2c8..647b5a72 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 @@ -66,6 +66,7 @@ public class PowerAuthSDK { private final @NonNull ReentrantLock mLock; private final @NonNull Session mSession; private final @NonNull PowerAuthConfiguration mConfiguration; + private final @NonNull PowerAuthBiometricConfiguration mBiometricConfiguration; private final @NonNull PowerAuthKeychainConfiguration mKeychainConfiguration; private final @NonNull IExecutorProvider mExecutorProvider; private final @NonNull HttpClient mClient; @@ -85,6 +86,7 @@ public class PowerAuthSDK { public static class Builder { private final @NonNull PowerAuthConfiguration mConfiguration; + private PowerAuthBiometricConfiguration mBiometricConfiguration; private PowerAuthClientConfiguration mClientConfiguration; private PowerAuthKeychainConfiguration mKeychainConfiguration; private ISavePowerAuthStateListener mStateListener; @@ -99,6 +101,16 @@ public Builder(@NonNull PowerAuthConfiguration configuration) { this.mConfiguration = configuration; } + /** + * Set custom biometric configuration. + * @param biometricConfiguration Biometric configuration. + * @return {@link Builder} + */ + public @NonNull Builder biometricConfiguration(@NonNull PowerAuthBiometricConfiguration biometricConfiguration) { + this.mBiometricConfiguration = biometricConfiguration; + return this; + } + /** * Set custom configuration for RESTful API client. * @param configuration Configuration for RESTful API client. @@ -174,6 +186,16 @@ public PowerAuthSDK build(@NonNull Context context) throws PowerAuthErrorExcepti } // Create default configuration objects + if (mBiometricConfiguration == null) { + if (mKeychainConfiguration == null) { + // No config object provided, use default biometric configuration. + mBiometricConfiguration = new PowerAuthBiometricConfiguration.Builder().build(); + } else { + // As fallback, construct biometric configuration from the keychain configuration. + // @Deprecated // 1.10.0 + mBiometricConfiguration = new PowerAuthBiometricConfiguration(mKeychainConfiguration); + } + } if (mKeychainConfiguration == null) { mKeychainConfiguration = new PowerAuthKeychainConfiguration.Builder().build(); } @@ -226,6 +248,7 @@ public PowerAuthSDK build(@NonNull Context context) throws PowerAuthErrorExcepti sharedLock, session, mConfiguration, + mBiometricConfiguration, mKeychainConfiguration, executorProvider, httpClient, @@ -253,6 +276,7 @@ public PowerAuthSDK build(@NonNull Context context) throws PowerAuthErrorExcepti * @param sharedLock Reentrant lock shared between various internal classes. * @param session Low-level {@link Session} instance. * @param configuration Main {@link PowerAuthConfiguration}. + * @param biometricConfiguration Biometric configuration. * @param keychainConfiguration Keychain configuration. * @param executorProvider Thread executor provider. * @param client HTTP client implementation. @@ -269,6 +293,7 @@ private PowerAuthSDK( @NonNull ReentrantLock sharedLock, @NonNull Session session, @NonNull PowerAuthConfiguration configuration, + @NonNull PowerAuthBiometricConfiguration biometricConfiguration, @NonNull PowerAuthKeychainConfiguration keychainConfiguration, @NonNull IExecutorProvider executorProvider, @NonNull HttpClient client, @@ -284,6 +309,7 @@ private PowerAuthSDK( this.mLock = sharedLock; this.mSession = session; this.mConfiguration = configuration; + this.mBiometricConfiguration = biometricConfiguration; this.mKeychainConfiguration = keychainConfiguration; this.mExecutorProvider = executorProvider; this.mClient = client; @@ -558,6 +584,27 @@ public void onCancel() { return mConfiguration; } + /** + * @return Biometric configuration provided during the SDK object construction. + */ + public @NonNull PowerAuthBiometricConfiguration getBiometricConfiguration() { + return mBiometricConfiguration; + } + + /** + * @return Client configuration provided during the SDK object construction. + */ + public @NonNull PowerAuthClientConfiguration getClientConfiguration() { + return mClient.getClientConfiguration(); + } + + /** + * @return Keychain configuration provided during the SDK object construction. + */ + public @NonNull PowerAuthKeychainConfiguration getKeychainConfiguration() { + return mKeychainConfiguration; + } + /** * The method is used for saving serialized state of Session, for example after password change method called directly via Session instance. See {@link PowerAuthSDK#getSession()} method. */ @@ -2240,8 +2287,8 @@ private ICancelable authenticateUsingBiometrics( .setDescription(description) .setRawKeyData(rawKeyData) .setKeystoreAlias(biometricDataMapping.keystoreId) - .setForceGenerateNewKey(forceGenerateNewKey, mKeychainConfiguration.isLinkBiometricItemsToCurrentSet(), mKeychainConfiguration.isAuthenticateOnBiometricKeySetup()) - .setUserConfirmationRequired(mKeychainConfiguration.isConfirmBiometricAuthentication()) + .setForceGenerateNewKey(forceGenerateNewKey, mBiometricConfiguration.isInvalidateBiometricFactorAfterChange(), mBiometricConfiguration.isAuthenticateOnBiometricKeySetup()) + .setUserConfirmationRequired(mBiometricConfiguration.isConfirmBiometricAuthentication()) .setBackgroundTaskExecutor(mExecutorProvider.getConcurrentExecutor()); if (fragmentHelper.getFragment() != null) { authenticationRequestBuilder.setFragment(fragmentHelper.getFragment());