Checking for iOS device first unlock to determine if items stored with kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly are available
Asked Answered
I

1

9

Assume a following situation:

  1. User restarts his/her iPhone.
  2. User lets device locked, does not unlock it.
  3. Server sends a (silent) push notification on the device (or anything happens that wakes the application up on the background, such as Apple Watch extension requests data, etc.).
  4. App wakes up and tries to access Keychain items stored with kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly accessibility.

Now, the Keychain items should not be accessible, since device was not unlocked yet. How do I correctly check for this situation?

Note: In my case, the presence of an item stored in the keychain determines if the app is "active" or not, so I would need something to stop this check soon enough, otherwise my app will assume it's not active (value cannot be read) and perform init steps...

Inflatable answered 15/11, 2016 at 16:6 Comment(0)
G
5

I've come across the same situation in my app and here's how I'm checking if the keychain is available (objective-c code):

+ (BOOL)isKeychainAvailable {
    NSString *testVal = @"testVal";
    NSString *testKey = @"testKey";
    [JNKeychain saveValue:testVal forKey:testKey];
    NSString *validatedValue = [JNKeychain loadValueForKey:testKey];
    [JNKeychain deleteValueForKey:testKey];
    return (validatedValue == testVal);
}

I basically save a value in the keychain and try to read it again. If it's not the same as I've just written it means the keychain is unavailable, which also means the phone hasn't been through the first unlock since the keychain should be available after the first unlock thanks to the option kSecAttrAccessibleAfterFirstUnlock.

What I ended up doing in this situation is terminating the app if it was started in the background and the keychain is unavailable:

- (void) methodStartedInBackgroundThatNeedsKeychain {
    if (!JNKeychain.isKeychainAvailable && [UIApplication sharedApplication].applicationState != UIApplicationStateActive) {
        exit(0);
    }
}

ATTENTION! please be aware that Apple strongly discourages the usage of exit(0) when the app is in foreground mode, thats why I'm making sure to only call it in background with [UIApplication sharedApplication].applicationState != UIApplicationStateActive. Here's Apple's QA discussion on the subject: https://developer.apple.com/library/archive/qa/qa1561/_index.html

Gregorygregrory answered 12/9, 2019 at 11:32 Comment(1)
I will accept this answer - I like the gist of the solution (store the value with the kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly flag and try to read it back immediately), clever! Thank you.Indonesian

© 2022 - 2024 — McMap. All rights reserved.