From 8624c6bb85845276fb424ebd30ddc83d2da9adfb Mon Sep 17 00:00:00 2001 From: Herbert Poul Date: Mon, 18 Jul 2022 21:43:00 +0200 Subject: [PATCH 1/6] deprecate authenticationValidityDurationSeconds in favor of androidAuthenticationValidityDuration --- .../biometric_storage/BiometricStorageFile.kt | 15 +++++++++------ .../biometric_storage/BiometricStoragePlugin.kt | 3 +++ lib/src/biometric_storage.dart | 16 +++++++++++++--- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/android/src/main/kotlin/design/codeux/biometric_storage/BiometricStorageFile.kt b/android/src/main/kotlin/design/codeux/biometric_storage/BiometricStorageFile.kt index 086752e..1f15074 100644 --- a/android/src/main/kotlin/design/codeux/biometric_storage/BiometricStorageFile.kt +++ b/android/src/main/kotlin/design/codeux/biometric_storage/BiometricStorageFile.kt @@ -8,11 +8,12 @@ import mu.KotlinLogging import java.io.File import java.io.IOException import javax.crypto.Cipher +import kotlin.time.Duration private val logger = KotlinLogging.logger {} data class InitOptions( - val authenticationValidityDurationSeconds: Int = -1, + val androidAuthenticationValidityDuration: Duration? = null, val authenticationRequired: Boolean = true, val androidBiometricOnly: Boolean = true ) @@ -44,20 +45,22 @@ class BiometricStorageFile( setIsStrongBoxBacked(useStrongBox) } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - if (options.authenticationValidityDurationSeconds == -1) { + if (options.androidAuthenticationValidityDuration == null) { setUserAuthenticationParameters( 0, KeyProperties.AUTH_BIOMETRIC_STRONG ) } else { setUserAuthenticationParameters( - options.authenticationValidityDurationSeconds, + options.androidAuthenticationValidityDuration.inWholeSeconds.toInt(), KeyProperties.AUTH_DEVICE_CREDENTIAL or KeyProperties.AUTH_BIOMETRIC_STRONG ) } } else { @Suppress("DEPRECATION") - setUserAuthenticationValidityDurationSeconds(options.authenticationValidityDurationSeconds) + setUserAuthenticationValidityDurationSeconds( + options.androidAuthenticationValidityDuration?.inWholeSeconds?.toInt() ?: -1 + ) } } @@ -74,8 +77,8 @@ class BiometricStorageFile( } private fun validateOptions() { - if (options.authenticationValidityDurationSeconds == -1 && !options.androidBiometricOnly) { - throw IllegalArgumentException("when authenticationValidityDurationSeconds is -1, androidBiometricOnly must be true") + if (options.androidAuthenticationValidityDuration == null && !options.androidBiometricOnly) { + throw IllegalArgumentException("when androidAuthenticationValidityDuration is null, androidBiometricOnly must be true") } } diff --git a/android/src/main/kotlin/design/codeux/biometric_storage/BiometricStoragePlugin.kt b/android/src/main/kotlin/design/codeux/biometric_storage/BiometricStoragePlugin.kt index 4548897..877fa81 100644 --- a/android/src/main/kotlin/design/codeux/biometric_storage/BiometricStoragePlugin.kt +++ b/android/src/main/kotlin/design/codeux/biometric_storage/BiometricStoragePlugin.kt @@ -22,6 +22,8 @@ import java.io.StringWriter import java.util.concurrent.ExecutorService import java.util.concurrent.Executors import javax.crypto.Cipher +import kotlin.time.Duration +import kotlin.time.Duration.Companion.seconds private val logger = KotlinLogging.logger {} @@ -223,6 +225,7 @@ class BiometricStoragePlugin : FlutterPlugin, ActivityAware, MethodCallHandler { val options = call.argument>("options")?.let { it -> InitOptions( authenticationValidityDurationSeconds = it["authenticationValidityDurationSeconds"] as Int, + androidAuthenticationValidityDuration = (it["androidAuthenticationValidityDurationSeconds"] as Int?)?.seconds, authenticationRequired = it["authenticationRequired"] as Boolean, androidBiometricOnly = it["androidBiometricOnly"] as Boolean, ) diff --git a/lib/src/biometric_storage.dart b/lib/src/biometric_storage.dart index 5646966..fecb44a 100644 --- a/lib/src/biometric_storage.dart +++ b/lib/src/biometric_storage.dart @@ -80,13 +80,21 @@ class AuthException implements Exception { class StorageFileInitOptions { StorageFileInitOptions({ + Duration? androidAuthenticationValidityDuration, this.authenticationValidityDurationSeconds = -1, this.authenticationRequired = true, this.androidBiometricOnly = true, - }); + }) : androidAuthenticationValidityDuration = + androidAuthenticationValidityDuration ?? + (authenticationValidityDurationSeconds <= 0 + ? null + : Duration(seconds: authenticationValidityDurationSeconds)); + @Deprecated('use ') final int authenticationValidityDurationSeconds; + final Duration? androidAuthenticationValidityDuration; + /// Whether an authentication is required. if this is /// false NO BIOMETRIC CHECK WILL BE PERFORMED! and the value /// will simply be save encrypted. (default: true) @@ -97,14 +105,16 @@ class StorageFileInitOptions { /// On Android < 30 this will always be ignored. (always `true`) /// https://github.com/authpass/biometric_storage/issues/12#issuecomment-900358154 /// - /// Also: this **must** be `true` if [authenticationValidityDurationSeconds] - /// is `-1`. + /// Also: this **must** be `true` if [androidAuthenticationValidityDuration] + /// is null. /// https://github.com/authpass/biometric_storage/issues/12#issuecomment-902508609 final bool androidBiometricOnly; Map toJson() => { 'authenticationValidityDurationSeconds': authenticationValidityDurationSeconds, + 'androidAuthenticationValidityDurationSeconds': + androidAuthenticationValidityDuration?.inSeconds, 'authenticationRequired': authenticationRequired, 'androidBiometricOnly': androidBiometricOnly, }; From 6e954baae7460f2dcedf8208a4258bd0979024f7 Mon Sep 17 00:00:00 2001 From: Herbert Poul Date: Mon, 18 Jul 2022 22:07:58 +0200 Subject: [PATCH 2/6] introduce iosTouchIDAuthenticationAllowableReuseDuration and iosTouchIDAuthenticationForceReuseContextDuration --- lib/src/biometric_storage.dart | 32 +++++++++++++++-- macos/Classes/BiometricStorageImpl.swift | 44 +++++++++++++++++------- 2 files changed, 62 insertions(+), 14 deletions(-) diff --git a/lib/src/biometric_storage.dart b/lib/src/biometric_storage.dart index fecb44a..e554871 100644 --- a/lib/src/biometric_storage.dart +++ b/lib/src/biometric_storage.dart @@ -81,20 +81,44 @@ class AuthException implements Exception { class StorageFileInitOptions { StorageFileInitOptions({ Duration? androidAuthenticationValidityDuration, + Duration? iosTouchIDAuthenticationAllowableReuseDuration, + this.iosTouchIDAuthenticationForceReuseContextDuration, this.authenticationValidityDurationSeconds = -1, this.authenticationRequired = true, this.androidBiometricOnly = true, - }) : androidAuthenticationValidityDuration = + }) : androidAuthenticationValidityDuration = androidAuthenticationValidityDuration ?? + (authenticationValidityDurationSeconds <= 0 + ? null + : Duration(seconds: authenticationValidityDurationSeconds)), + iosTouchIDAuthenticationAllowableReuseDuration = + iosTouchIDAuthenticationAllowableReuseDuration ?? (authenticationValidityDurationSeconds <= 0 ? null : Duration(seconds: authenticationValidityDurationSeconds)); - @Deprecated('use ') + @Deprecated('use androidAuthenticationValidityDuration instead') final int authenticationValidityDurationSeconds; + /// see https://developer.android.com/reference/android/security/keystore/KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int,%20int) final Duration? androidAuthenticationValidityDuration; + /// see https://developer.apple.com/documentation/localauthentication/lacontext/1622329-touchidauthenticationallowablere + /// > If the user unlocks the device using Touch ID within the specified time interval, then authentication for the receiver succeeds automatically, without prompting the user for Touch ID. This bypasses a scenario where the user unlocks the device and then is almost immediately prompted for another fingerprint. + /// and https://developer.apple.com/documentation/localauthentication/accessing_keychain_items_with_face_id_or_touch_id + /// > Note that this grace period applies specifically to device unlock with Touch ID, not keychain retrieval authentications + /// + /// If you want to avoid requiring authentication after a successful + /// keychain retrieval see [iosTouchIDAuthenticationForceReuseContextDuration] + final Duration? iosTouchIDAuthenticationAllowableReuseDuration; + + /// To prevent forcing the user to authenticate again after unlocking once + /// we can reuse the `LAContext` object for the given amount of time. + /// see https://github.com/authpass/biometric_storage/pull/73 + /// This is pretty much undocumented behavior, but works similar to + /// `androidAuthenticationValidityDuration`. + final Duration? iosTouchIDAuthenticationForceReuseContextDuration; + /// Whether an authentication is required. if this is /// false NO BIOMETRIC CHECK WILL BE PERFORMED! and the value /// will simply be save encrypted. (default: true) @@ -115,6 +139,10 @@ class StorageFileInitOptions { authenticationValidityDurationSeconds, 'androidAuthenticationValidityDurationSeconds': androidAuthenticationValidityDuration?.inSeconds, + 'iosTouchIDAuthenticationAllowableReuseDurationSeconds': + iosTouchIDAuthenticationAllowableReuseDuration?.inSeconds, + 'iosTouchIDAuthenticationForceReuseContextDurationSeconds': + iosTouchIDAuthenticationForceReuseContextDuration?.inSeconds, 'authenticationRequired': authenticationRequired, 'androidBiometricOnly': androidBiometricOnly, }; diff --git a/macos/Classes/BiometricStorageImpl.swift b/macos/Classes/BiometricStorageImpl.swift index a2782fc..a99a96a 100644 --- a/macos/Classes/BiometricStorageImpl.swift +++ b/macos/Classes/BiometricStorageImpl.swift @@ -14,10 +14,12 @@ struct StorageMethodCall { class InitOptions { init(params: [String: Any]) { - authenticationValidityDurationSeconds = params["authenticationValidityDurationSeconds"] as? Int + iosTouchIDAuthenticationAllowableReuseDuration = params["iosTouchIDAuthenticationAllowableReuseDurationSeconds"] as? Int + iosTouchIDAuthenticationForceReuseContextDuration = params["iosTouchIDAuthenticationForceReuseContextDurationSeconds"] as? Int authenticationRequired = params["authenticationRequired"] as? Bool } - let authenticationValidityDurationSeconds: Int! + let iosTouchIDAuthenticationAllowableReuseDuration: Int? + let iosTouchIDAuthenticationForceReuseContextDuration: Int? let authenticationRequired: Bool! } @@ -148,23 +150,41 @@ class BiometricStorageImpl { } } +typealias StoredContext = (context: LAContext, expireAt: Date) + class BiometricStorageFile { private let name: String private let initOptions: InitOptions - private var context: LAContext { get { - let context = LAContext() - if (initOptions.authenticationRequired) { - if initOptions.authenticationValidityDurationSeconds > 0 { - if #available(OSX 10.12, *) { - context.touchIDAuthenticationAllowableReuseDuration = Double(initOptions.authenticationValidityDurationSeconds) + private var _context: StoredContext? + private var context: LAContext { + get { + if let context = _context { + if context.expireAt.timeIntervalSinceNow < 0 { + // already expired. + _context = nil } else { - // Fallback on earlier versions - hpdebug("Pre OSX 10.12 no touchIDAuthenticationAllowableReuseDuration available. ignoring.") + return context.context } } + + let context = LAContext() + if (initOptions.authenticationRequired) { + if let duration = initOptions.iosTouchIDAuthenticationAllowableReuseDuration { + if #available(OSX 10.12, *) { + context.touchIDAuthenticationAllowableReuseDuration = Double(duration) + } else { + // Fallback on earlier versions + hpdebug("Pre OSX 10.12 no touchIDAuthenticationAllowableReuseDuration available. ignoring.") + } + } + + if let duration = initOptions.iosTouchIDAuthenticationForceReuseContextDuration { + _context = (context: context, expireAt: Date(timeIntervalSinceNow: Double(duration))) + } + } + return context } - return context - } } + } private let storageError: StorageError init(name: String, initOptions: InitOptions, storageError: @escaping StorageError) { From a2ec96837d493be9c09f244010c995e9362f8804 Mon Sep 17 00:00:00 2001 From: Herbert Poul Date: Mon, 18 Jul 2022 22:20:22 +0200 Subject: [PATCH 3/6] fix android compilation --- .../codeux/biometric_storage/BiometricStoragePlugin.kt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/android/src/main/kotlin/design/codeux/biometric_storage/BiometricStoragePlugin.kt b/android/src/main/kotlin/design/codeux/biometric_storage/BiometricStoragePlugin.kt index 877fa81..7fc720b 100644 --- a/android/src/main/kotlin/design/codeux/biometric_storage/BiometricStoragePlugin.kt +++ b/android/src/main/kotlin/design/codeux/biometric_storage/BiometricStoragePlugin.kt @@ -178,7 +178,7 @@ class BiometricStoragePlugin : FlutterPlugin, ActivityAware, MethodCallHandler { CipherMode.Decrypt -> cipherForDecrypt() } - val cipher = if (options.authenticationValidityDurationSeconds > -1) { + val cipher = if (options.androidAuthenticationValidityDuration != null) { null } else try { cipherForMode() @@ -224,7 +224,6 @@ class BiometricStoragePlugin : FlutterPlugin, ActivityAware, MethodCallHandler { val options = call.argument>("options")?.let { it -> InitOptions( - authenticationValidityDurationSeconds = it["authenticationValidityDurationSeconds"] as Int, androidAuthenticationValidityDuration = (it["androidAuthenticationValidityDurationSeconds"] as Int?)?.seconds, authenticationRequired = it["authenticationRequired"] as Boolean, androidBiometricOnly = it["androidBiometricOnly"] as Boolean, @@ -401,9 +400,9 @@ class BiometricStoragePlugin : FlutterPlugin, ActivityAware, MethodCallHandler { promptBuilder.setAllowedAuthenticators(DEVICE_CREDENTIAL or BIOMETRIC_STRONG) } - if (cipher == null || options.authenticationValidityDurationSeconds >= 0) { - // if authenticationValidityDurationSeconds is not -1 we can't use a CryptoObject - logger.debug { "Authenticating without cipher. ${options.authenticationValidityDurationSeconds}" } + if (cipher == null || options.androidAuthenticationValidityDuration != null) { + // if androidAuthenticationValidityDuration is not null we can't use a CryptoObject + logger.debug { "Authenticating without cipher. ${options.androidAuthenticationValidityDuration}" } prompt.authenticate(promptBuilder.build()) } else { prompt.authenticate(promptBuilder.build(), BiometricPrompt.CryptoObject(cipher)) From 9120b6752ef3a32eab0ff8a62a267600e07845a5 Mon Sep 17 00:00:00 2001 From: Herbert Poul Date: Tue, 21 May 2024 10:53:52 +0200 Subject: [PATCH 4/6] rename ios* parameters to darwin* --- example/ios/Podfile.lock | 2 +- example/ios/Runner.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- example/pubspec.lock | 56 +++++++++++++------ lib/src/biometric_storage.dart | 21 +++---- macos/Classes/BiometricStorageImpl.swift | 12 ++-- 6 files changed, 60 insertions(+), 35 deletions(-) diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index e9c8ef2..68eab1b 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -19,4 +19,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: c4c93c5f6502fe2754f48404d3594bf779584011 -COCOAPODS: 1.15.0 +COCOAPODS: 1.15.2 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 1772d3e..77ec0c9 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -163,7 +163,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = "The Chromium Authors"; TargetAttributes = { 97C146ED1CF9000F007C117D = { diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index b52b2e6..e67b280 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ =3.2.0 <4.0.0" - flutter: ">=2.8.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/lib/src/biometric_storage.dart b/lib/src/biometric_storage.dart index cfeeb18..f28af28 100644 --- a/lib/src/biometric_storage.dart +++ b/lib/src/biometric_storage.dart @@ -86,7 +86,7 @@ class StorageFileInitOptions { StorageFileInitOptions({ Duration? androidAuthenticationValidityDuration, Duration? iosTouchIDAuthenticationAllowableReuseDuration, - this.iosTouchIDAuthenticationForceReuseContextDuration, + this.darwinTouchIDAuthenticationForceReuseContextDuration, this.authenticationValidityDurationSeconds = -1, this.authenticationRequired = true, this.androidBiometricOnly = true, @@ -96,13 +96,14 @@ class StorageFileInitOptions { (authenticationValidityDurationSeconds <= 0 ? null : Duration(seconds: authenticationValidityDurationSeconds)), - iosTouchIDAuthenticationAllowableReuseDuration = + darwinTouchIDAuthenticationAllowableReuseDuration = iosTouchIDAuthenticationAllowableReuseDuration ?? (authenticationValidityDurationSeconds <= 0 ? null : Duration(seconds: authenticationValidityDurationSeconds)); - @Deprecated('use androidAuthenticationValidityDuration instead') + @Deprecated( + 'use use androidAuthenticationValidityDuration, iosTouchIDAuthenticationAllowableReuseDuration or iosTouchIDAuthenticationForceReuseContextDuration instead') final int authenticationValidityDurationSeconds; /// see https://developer.android.com/reference/android/security/keystore/KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int,%20int) @@ -114,15 +115,15 @@ class StorageFileInitOptions { /// > Note that this grace period applies specifically to device unlock with Touch ID, not keychain retrieval authentications /// /// If you want to avoid requiring authentication after a successful - /// keychain retrieval see [iosTouchIDAuthenticationForceReuseContextDuration] - final Duration? iosTouchIDAuthenticationAllowableReuseDuration; + /// keychain retrieval see [darwinTouchIDAuthenticationForceReuseContextDuration] + final Duration? darwinTouchIDAuthenticationAllowableReuseDuration; /// To prevent forcing the user to authenticate again after unlocking once /// we can reuse the `LAContext` object for the given amount of time. /// see https://github.com/authpass/biometric_storage/pull/73 /// This is pretty much undocumented behavior, but works similar to /// `androidAuthenticationValidityDuration`. - final Duration? iosTouchIDAuthenticationForceReuseContextDuration; + final Duration? darwinTouchIDAuthenticationForceReuseContextDuration; /// Whether an authentication is required. if this is /// false NO BIOMETRIC CHECK WILL BE PERFORMED! and the value @@ -149,10 +150,10 @@ class StorageFileInitOptions { authenticationValidityDurationSeconds, 'androidAuthenticationValidityDurationSeconds': androidAuthenticationValidityDuration?.inSeconds, - 'iosTouchIDAuthenticationAllowableReuseDurationSeconds': - iosTouchIDAuthenticationAllowableReuseDuration?.inSeconds, - 'iosTouchIDAuthenticationForceReuseContextDurationSeconds': - iosTouchIDAuthenticationForceReuseContextDuration?.inSeconds, + 'darwinTouchIDAuthenticationAllowableReuseDurationSeconds': + darwinTouchIDAuthenticationAllowableReuseDuration?.inSeconds, + 'darwinTouchIDAuthenticationForceReuseContextDurationSeconds': + darwinTouchIDAuthenticationForceReuseContextDuration?.inSeconds, 'authenticationRequired': authenticationRequired, 'androidBiometricOnly': androidBiometricOnly, 'darwinBiometricOnly': darwinBiometricOnly, diff --git a/macos/Classes/BiometricStorageImpl.swift b/macos/Classes/BiometricStorageImpl.swift index 80ad834..1cc1869 100644 --- a/macos/Classes/BiometricStorageImpl.swift +++ b/macos/Classes/BiometricStorageImpl.swift @@ -14,13 +14,13 @@ struct StorageMethodCall { class InitOptions { init(params: [String: Any]) { - iosTouchIDAuthenticationAllowableReuseDuration = params["iosTouchIDAuthenticationAllowableReuseDurationSeconds"] as? Int - iosTouchIDAuthenticationForceReuseContextDuration = params["iosTouchIDAuthenticationForceReuseContextDurationSeconds"] as? Int + darwinTouchIDAuthenticationAllowableReuseDuration = params["drawinTouchIDAuthenticationAllowableReuseDurationSeconds"] as? Int + darwinTouchIDAuthenticationForceReuseContextDuration = params["darwinTouchIDAuthenticationForceReuseContextDurationSeconds"] as? Int authenticationRequired = params["authenticationRequired"] as? Bool darwinBiometricOnly = params["darwinBiometricOnly"] as? Bool } - let iosTouchIDAuthenticationAllowableReuseDuration: Int? - let iosTouchIDAuthenticationForceReuseContextDuration: Int? + let darwinTouchIDAuthenticationAllowableReuseDuration: Int? + let darwinTouchIDAuthenticationForceReuseContextDuration: Int? let authenticationRequired: Bool! let darwinBiometricOnly: Bool! } @@ -173,7 +173,7 @@ class BiometricStorageFile { let context = LAContext() if (initOptions.authenticationRequired) { - if let duration = initOptions.iosTouchIDAuthenticationAllowableReuseDuration { + if let duration = initOptions.darwinTouchIDAuthenticationAllowableReuseDuration { if #available(OSX 10.12, *) { context.touchIDAuthenticationAllowableReuseDuration = Double(duration) } else { @@ -182,7 +182,7 @@ class BiometricStorageFile { } } - if let duration = initOptions.iosTouchIDAuthenticationForceReuseContextDuration { + if let duration = initOptions.darwinTouchIDAuthenticationForceReuseContextDuration { _context = (context: context, expireAt: Date(timeIntervalSinceNow: Double(duration))) } } From a49861960350d154d7588d0c0e2d47686335e517 Mon Sep 17 00:00:00 2001 From: Herbert Poul Date: Tue, 21 May 2024 11:09:34 +0200 Subject: [PATCH 5/6] update example, some documentation. --- example/analysis_options.yaml | 19 +++++++++++++ example/lib/main.dart | 50 ++++++++++++++++++++-------------- example/pubspec.lock | 26 +++++++++--------- example/pubspec.yaml | 2 +- example/test/widget_test.dart | 2 +- lib/src/biometric_storage.dart | 10 ++++--- pubspec.yaml | 4 +-- 7 files changed, 71 insertions(+), 42 deletions(-) create mode 100644 example/analysis_options.yaml diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml new file mode 100644 index 0000000..0ba1255 --- /dev/null +++ b/example/analysis_options.yaml @@ -0,0 +1,19 @@ +include: package:flutter_lints/flutter.yaml + +analyzer: + errors: + # treat missing required parameters as a warning (not a hint) + missing_required_param: warning + # treat missing returns as a warning (not a hint) + missing_return: warning + # allow having TODOs in the code + todo: ignore + exclude: + - lib/generated_plugin_registrant.dart + - example/lib/generated_plugin_registrant.dart + language: + strict-casts: true + strict-raw-types: true + +linter: + rules: diff --git a/example/lib/main.dart b/example/lib/main.dart index 46459c3..15ee52a 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -15,7 +15,7 @@ void main() { PrintAppender().attachToLogger(Logger.root); logMessages.attachToLogger(Logger.root); _logger.fine('Application launched. (v2)'); - runApp(MyApp()); + runApp(const MyApp()); } class StringBufferWrapper with ChangeNotifier { @@ -62,6 +62,8 @@ class MemoryAppender extends BaseLogAppender { } class MyApp extends StatefulWidget { + const MyApp({super.key}); + @override MyAppState createState() => MyAppState(); } @@ -130,22 +132,26 @@ class MyAppState extends State { authenticationRequired: false, )); if (supportsAuthenticated) { - _customPrompt = await BiometricStorage().getStorage( - '${baseName}_customPrompt', - options: StorageFileInitOptions( - authenticationValidityDurationSeconds: 5), - promptInfo: const PromptInfo( - iosPromptInfo: IosPromptInfo( - saveTitle: 'Custom save title', - accessTitle: 'Custom access title.', - ), - androidPromptInfo: AndroidPromptInfo( - title: 'Custom title', - subtitle: 'Custom subtitle', - description: 'Custom description', - negativeButton: 'Nope!', - ), - )); + _customPrompt = await BiometricStorage() + .getStorage('${baseName}_customPrompt', + options: StorageFileInitOptions( + androidAuthenticationValidityDuration: + const Duration(seconds: 5), + darwinTouchIDAuthenticationAllowableReuseDuration: + const Duration(seconds: 5), + ), + promptInfo: const PromptInfo( + iosPromptInfo: IosPromptInfo( + saveTitle: 'Custom save title', + accessTitle: 'Custom access title.', + ), + androidPromptInfo: AndroidPromptInfo( + title: 'Custom title', + subtitle: 'Custom subtitle', + description: 'Custom description', + negativeButton: 'Nope!', + ), + )); } setState(() {}); _logger.info('initiailzed $baseName'); @@ -194,13 +200,13 @@ class MyAppState extends State { color: Colors.white, constraints: const BoxConstraints.expand(), child: SingleChildScrollView( + reverse: true, child: Container( padding: const EdgeInsets.all(16), child: Text( logMessages.log.toString(), ), ), - reverse: true, ), ), ), @@ -230,9 +236,11 @@ class MyAppState extends State { } class StorageActions extends StatelessWidget { - const StorageActions( - {Key? key, required this.storageFile, required this.writeController}) - : super(key: key); + const StorageActions({ + super.key, + required this.storageFile, + required this.writeController, + }); final BiometricStorageFile storageFile; final TextEditingController writeController; diff --git a/example/pubspec.lock b/example/pubspec.lock index 8472261..ac3fa63 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -52,10 +52,10 @@ packages: dependency: transitive description: name: dio - sha256: "797e1e341c3dd2f69f2dad42564a6feff3bfb87187d05abb93b9609e6f1645c3" + sha256: "11e40df547d418cc0c4900a9318b26304e665da6fa4755399a9ff9efd09034b5" url: "https://pub.dev" source: hosted - version: "5.4.0" + version: "5.4.3+1" fake_async: dependency: transitive description: @@ -68,10 +68,10 @@ packages: dependency: transitive description: name: ffi - sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" + sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.2" flutter: dependency: "direct main" description: flutter @@ -81,10 +81,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 + sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "4.0.0" flutter_test: dependency: "direct dev" description: flutter @@ -139,10 +139,10 @@ packages: dependency: transitive description: name: lints - sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 + sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "4.0.0" logging: dependency: "direct main" description: @@ -155,10 +155,10 @@ packages: dependency: "direct main" description: name: logging_appenders - sha256: "1fb8a008c04246f4677a0d034d69779a5975e56e02573a5162240239b247e239" + sha256: cc16fd4ecf92ae8b45294ad01849468475b65c4aadac57c2ed3e6cc3f91b557c url: "https://pub.dev" source: hosted - version: "1.2.0+1" + version: "1.3.0" matcher: dependency: transitive description: @@ -280,10 +280,10 @@ packages: dependency: transitive description: name: win32 - sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8" + sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4 url: "https://pub.dev" source: hosted - version: "5.2.0" + version: "5.5.1" sdks: - dart: ">=3.3.0 <4.0.0" + dart: ">=3.4.0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index b9578db..0229490 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -17,7 +17,7 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^3.0.1 + flutter_lints: ^4.0.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart index 1a81581..82cc80b 100644 --- a/example/test/widget_test.dart +++ b/example/test/widget_test.dart @@ -12,7 +12,7 @@ import 'package:flutter_test/flutter_test.dart'; void main() { testWidgets('Verify Platform version', (WidgetTester tester) async { // Build our app and trigger a frame. - await tester.pumpWidget(MyApp()); + await tester.pumpWidget(const MyApp()); // Verify that platform version is retrieved. expect( diff --git a/lib/src/biometric_storage.dart b/lib/src/biometric_storage.dart index f28af28..f07bf0c 100644 --- a/lib/src/biometric_storage.dart +++ b/lib/src/biometric_storage.dart @@ -85,8 +85,10 @@ class AuthException implements Exception { class StorageFileInitOptions { StorageFileInitOptions({ Duration? androidAuthenticationValidityDuration, - Duration? iosTouchIDAuthenticationAllowableReuseDuration, + Duration? darwinTouchIDAuthenticationAllowableReuseDuration, this.darwinTouchIDAuthenticationForceReuseContextDuration, + @Deprecated( + 'use use androidAuthenticationValidityDuration, iosTouchIDAuthenticationAllowableReuseDuration or iosTouchIDAuthenticationForceReuseContextDuration instead') this.authenticationValidityDurationSeconds = -1, this.authenticationRequired = true, this.androidBiometricOnly = true, @@ -97,7 +99,7 @@ class StorageFileInitOptions { ? null : Duration(seconds: authenticationValidityDurationSeconds)), darwinTouchIDAuthenticationAllowableReuseDuration = - iosTouchIDAuthenticationAllowableReuseDuration ?? + darwinTouchIDAuthenticationAllowableReuseDuration ?? (authenticationValidityDurationSeconds <= 0 ? null : Duration(seconds: authenticationValidityDurationSeconds)); @@ -123,6 +125,8 @@ class StorageFileInitOptions { /// see https://github.com/authpass/biometric_storage/pull/73 /// This is pretty much undocumented behavior, but works similar to /// `androidAuthenticationValidityDuration`. + /// + /// See also [darwinTouchIDAuthenticationAllowableReuseDuration] final Duration? darwinTouchIDAuthenticationForceReuseContextDuration; /// Whether an authentication is required. if this is @@ -146,8 +150,6 @@ class StorageFileInitOptions { final bool darwinBiometricOnly; Map toJson() => { - 'authenticationValidityDurationSeconds': - authenticationValidityDurationSeconds, 'androidAuthenticationValidityDurationSeconds': androidAuthenticationValidityDuration?.inSeconds, 'darwinTouchIDAuthenticationAllowableReuseDurationSeconds': diff --git a/pubspec.yaml b/pubspec.yaml index 1e8f409..1a10cb1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ dependencies: flutter_web_plugins: sdk: flutter logging: ">=1.0.0 <2.0.0" - plugin_platform_interface: ^2.0.0 + plugin_platform_interface: ">=2.0.0 <3.0.0" ffi: '>=1.0.0 <3.0.0' win32: '>=2.0.0 <6.0.0' @@ -23,7 +23,7 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^3.0.1 + flutter_lints: ^4.0.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec From 444e14138752899c72fce27a16d61f4572ce4636 Mon Sep 17 00:00:00 2001 From: Herbert Poul Date: Tue, 21 May 2024 11:13:42 +0200 Subject: [PATCH 6/6] update changelog. --- CHANGELOG.md | 6 ++++++ example/lib/main.dart | 2 +- pubspec.yaml | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b57819..3dc285b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 5.1.0-rc.1 + +* Split Split authenticationValidityDurationSeconds between android and iOS + * `darwinTouchIDAuthenticationForceReuseContextDuration`: Basically the equivalent to `androidAuthenticationValidityDuration` + * `darwinTouchIDAuthenticationAllowableReuseDuration` + ## 5.0.1 * Add option for iOS/MacOS to allow non-biometric authentication (`darwinBiometricOnly`) #101 diff --git a/example/lib/main.dart b/example/lib/main.dart index 15ee52a..de3042a 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -137,7 +137,7 @@ class MyAppState extends State { options: StorageFileInitOptions( androidAuthenticationValidityDuration: const Duration(seconds: 5), - darwinTouchIDAuthenticationAllowableReuseDuration: + darwinTouchIDAuthenticationForceReuseContextDuration: const Duration(seconds: 5), ), promptInfo: const PromptInfo( diff --git a/pubspec.yaml b/pubspec.yaml index 1a10cb1..db743b7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: biometric_storage description: | Secure Storage: Encrypted data store optionally secured by biometric lock with support for iOS, Android, MacOS. Partial support for Linux, Windows and web (localStorage). -version: 5.0.1 +version: 5.1.0-rc.1 homepage: https://github.com/authpass/biometric_storage/ environment: