Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determine if Touch ID-Protected Keychain Item Exists?

Is there a way to determine if an item (password, token, etc.) has been set in the iOS keychain using Touch ID access control without prompting the user for Touch ID? We have a need to determine if the credential has already been saved to the keychain (with Touch ID protection) before performing an operation, but we don't want to interrupt the user with the Touch ID prompt.

I've tried the following...

NSMutableDictionary *query = ...
query[(__bridge id)kSecUseNoAuthenticationUI] = (__bridge id)kCFBooleanTrue;

OSStatus opStatus = SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL);

...

However, when this code gets called the user still sees the Touch ID prompt. We don't want ANYTHING to be displayed on the UI, and just want an error returned in the OSStatus if Touch ID would have been required.

Any thoughts?

like image 675
Shadowman Avatar asked Dec 10 '15 17:12

Shadowman


People also ask

Is iOS keychain encrypted?

Everything stored in iCloud Keychain is secure—it's protected by industry-standard encryption. Your iCloud Keychain can't be set up on another Mac or iOS or iPadOS device unless you approve it.

Can you use keychain without ID?

Whenever you attempt to auto-fill passwords stored in iCloud Keychain, either into Safari or a third-party app, your iPhone or iPad will no longer require any form of authentication.

What is keychain in Swift?

The keychain is the best place to store small secrets, like passwords and cryptographic keys. You use the functions of the keychain services API to add, retrieve, delete, or modify keychain items.


2 Answers

based on neoneye's code and Swift 3. I've added errSecAuthFailed.

    query[kSecClass as String] : kSecClassGenericPassword,
    query[kSecAttrService as String] : "serviceName"    
    query[kSecUseAuthenticationUI as String] = kSecUseAuthenticationUIFail

    DispatchQueue.global().async {

        var result : AnyObject?
        let status = SecItemCopyMatching(query as CFDictionary, &result)

        if status == errSecInteractionNotAllowed {

            DispatchQueue.main.async {

                // item exists
            }
        } else if status == errSecAuthFailed {

            DispatchQueue.main.async {

                // item exists but someone removed the touch id or passcode
            }
        } else if status == errSecItemNotFound {

            DispatchQueue.main.async {

                // it does not exist
            }
        } else {

            DispatchQueue.main.async {

                // another OSStatus
            }
        }
    }
like image 192
wskcoder Avatar answered Oct 31 '22 22:10

wskcoder


NSDictionary *query = @{
                        (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
                        (__bridge id)kSecAttrService: @"SampleService",
                        (__bridge id)kSecUseNoAuthenticationUI: @YES
                        };

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    CFTypeRef dataTypeRef = NULL;
    OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)(query), &dataTypeRef);
    if (status == errSecInteractionNotAllowed) {
        NSLog(@"ITEM EXIST");
    } else if (status == errSecItemNotFound) {
        NSLog(@"ITEM DOES NOT EXIST");
    } else {
        NSLog(@"status: %@", @(status));
    }
});
like image 31
neoneye Avatar answered Oct 31 '22 22:10

neoneye