Skip to content

Commit

Permalink
WIP: refactor to always create context and auth options.
Browse files Browse the repository at this point in the history
  • Loading branch information
hpoul committed Jul 7, 2022
1 parent 9afb003 commit f514667
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 24 deletions.
4 changes: 2 additions & 2 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ class MyAppState extends State<MyApp> {
_customPrompt = await BiometricStorage().getStorage(
'${baseName}_customPrompt',
options: StorageFileInitOptions(
authenticationValidityDurationSeconds: 10),
authenticationValidityDurationSeconds: 5),
promptInfo: const PromptInfo(
iosPromptInfo: IosPromptInfo(
saveTitle: 'Custom save title',
Expand Down Expand Up @@ -175,7 +175,7 @@ class MyAppState extends State<MyApp> {
...?(_customPrompt == null
? null
: [
const Text('Custom Prompts w/ 10s auth validity',
const Text('Custom Prompts w/ 5s auth validity',
style: TextStyle(fontWeight: FontWeight.bold)),
StorageActions(
storageFile: _customPrompt!,
Expand Down
78 changes: 56 additions & 22 deletions macos/Classes/BiometricStorageImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ class BiometricStorageImpl {
class BiometricStorageFile {
private let name: String
private let initOptions: InitOptions
private lazy var context: LAContext = {
private var context: LAContext { get {
let context = LAContext()
if (initOptions.authenticationRequired) {
if initOptions.authenticationValidityDurationSeconds > 0 {
Expand All @@ -164,7 +164,7 @@ class BiometricStorageFile {
}
}
return context
}()
} }
private let storageError: StorageError

init(name: String, initOptions: InitOptions, storageError: @escaping StorageError) {
Expand All @@ -173,15 +173,56 @@ class BiometricStorageFile {
self.storageError = storageError
}

private func baseQuery() -> [String: Any] {
return [kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: "flutter_biometric_storage",
kSecAttrAccount as String: name]
private func baseQuery(_ result: @escaping StorageCallback) -> [String: Any]? {
var query = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: "flutter_biometric_storage",
kSecAttrAccount as String: name,
] as [String : Any]
if initOptions.authenticationRequired {
guard let access = accessControl(result) else {
return nil
}
if #available(iOS 13.0, *) {
query[kSecUseDataProtectionKeychain as String] = true
}
query[kSecAttrAccessControl as String] = access
}
return query
}

func read(_ result: @escaping StorageCallback, _ promptInfo: IOSPromptInfo) {
private func accessControl(_ result: @escaping StorageCallback) -> SecAccessControl? {
let accessControlFlags: SecAccessControlCreateFlags

var query = baseQuery()
if #available(iOS 11.3, *) {
accessControlFlags = .biometryCurrentSet
} else {
accessControlFlags = .touchIDCurrentSet
}

// access = SecAccessControlCreateWithFlags(nil,
// kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
// accessControlFlags,
// &error)
var error: Unmanaged<CFError>?
guard let access = SecAccessControlCreateWithFlags(
nil, // Use the default allocator.
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
accessControlFlags,
&error) else {
hpdebug("Error while creating access control flags. \(String(describing: error))")
result(storageError("writing data", "error writing data", "\(String(describing: error))"));
return nil
}

return access
}

func read(_ result: @escaping StorageCallback, _ promptInfo: IOSPromptInfo) {

guard var query = baseQuery(result) else {
return;
}
query[kSecMatchLimit as String] = kSecMatchLimitOne
query[kSecUseOperationPrompt as String] = promptInfo.accessTitle
query[kSecReturnAttributes as String] = true
Expand Down Expand Up @@ -210,7 +251,9 @@ class BiometricStorageFile {
}

func delete(_ result: @escaping StorageCallback, _ promptInfo: IOSPromptInfo) {
let query = baseQuery()
guard let query = baseQuery(result) else {
return;
}
// query[kSecMatchLimit as String] = kSecMatchLimitOne
// query[kSecReturnData as String] = true
let status = SecItemDelete(query as CFDictionary)
Expand All @@ -227,22 +270,13 @@ class BiometricStorageFile {
}

func write(_ content: String, _ result: @escaping StorageCallback, _ promptInfo: IOSPromptInfo) {
var query = baseQuery()

guard var query = baseQuery(result) else {
return;
}

if (initOptions.authenticationRequired) {
var error: Unmanaged<CFError>?
guard let access = SecAccessControlCreateWithFlags(
nil, // Use the default allocator.
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
.userPresence,
&error) else {
hpdebug("Error while creating access control flags. \(String(describing: error))")
result(storageError("writing data", "error writing data", "\(String(describing: error))"));
return;
}
query.merge([
kSecUseAuthenticationContext as String: context,
kSecAttrAccessControl as String: access as Any,
]) { (_, new) in new }
if let operationPrompt = promptInfo.saveTitle {
query[kSecUseOperationPrompt as String] = operationPrompt
Expand Down

0 comments on commit f514667

Please sign in to comment.