Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Catch Touch ID prompt canceled by user #535

Open
Ontokrat opened this issue Jan 19, 2022 · 1 comment
Open

Catch Touch ID prompt canceled by user #535

Ontokrat opened this issue Jan 19, 2022 · 1 comment

Comments

@Ontokrat
Copy link

Ontokrat commented Jan 19, 2022

Hello,

I can't manage to catch if a user canceled the touch ID / password prompt => the key is always returned, as nil if the user canceled the prompt.

That's a major issue because if the key is nil, I (later in the code) assume there is none and so I create it:
=> so in fact I modify it => meaning basically if the user cancel the prompt, its key is updated....

I'm working on a MacOS app.

Any idea? Thanks.

func findKeychainKey(identifier: String) throws -> Data? {
    let keychain = Keychain(service: "XXXX", accessGroup: "XXXX")
    do {
        let findKey = try keychain.getData(identifier)
        return findKey! // always ends here, either the key is found or not, or even if the user cancel the touch ID / password prompt
    } catch (Status.userCanceled) {
        // A user canceled the touch ID / password prompt
        print("The user canceled the touch ID / password prompt.")
        throw KeychainError.userCancelled
    } catch let error {
        print("error: \(error)")
        throw KeychainError.emptyKey
    }
}

Linked to #338

@Ontokrat
Copy link
Author

Ontokrat commented Jan 20, 2022

I've found the solution.
I believe it's only an issue on MacOS.

To resume the issue: when a user cancel the Touch ID / password prompt, SecItemCopyMatching is returning a errSecItemNotFound (-25300) instead of a errSecUserCanceled (-128).

case userCanceled = -128

case itemNotFound = -25300

Therefore the today code returns nil, not an error.

let status = SecItemCopyMatching(query as CFDictionary, &result)
switch status {
case errSecSuccess:
guard let data = result as? Data else {
throw Status.unexpectedError
}
return data
case errSecItemNotFound:
return nil
default:
throw securityError(status: status)

The query on MacOS needs the attribute kSecUseDataProtectionKeychain set to kCFBooleanTrue.
Available for macOS 10.15+.

A key whose value indicates whether to treat macOS keychain items like iOS keychain items.

The data protection key affects operations only in macOS. Other platforms automatically behave as if the key is set to true, and ignore the key in the query dictionary. You can safely use the key on all platforms.

Tip It’s highly recommended that you set the value of this key to true for all keychain operations. This key helps to improve the portability of your code across platforms. Use it unless you specifically need access to items previously stored in a legacy keychain in macOS.

I don't see it as an option in the lib, so it'll need a PR.

@Ontokrat Ontokrat changed the title Catch Touch ID prompt cancel Catch Touch ID prompt canceled Jan 20, 2022
@Ontokrat Ontokrat changed the title Catch Touch ID prompt canceled Catch Touch ID prompt canceled by user Jan 20, 2022
rivera-ernesto added a commit to codelathe/KeychainAccess that referenced this issue Feb 15, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant