I am currently storing the username (email) and a salted hash of the email and password in the iOS KeyChain. I'm using the ARC'ified version found here.
KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"MyCustomIdentifier" accessGroup:nil]; [wrapper setObject:APP_NAME forKey:(__bridge id)kSecAttrService]; [wrapper setObject:email forKey:(__bridge id)kSecAttrAccount]; [wrapper setObject:token forKey:(__bridge id)kSecValueData];
This all works fine when I need to pull the token out for my network calls while the app is active. It works for logging in from a clean startup, as well as all the network calls throughout. The trouble starts when the app is in the background.
Keep in mind, this only happens sporadically and I have yet to pin it down to a specific iOS version or device.
The user trips a location (region monitoring) and I want to update the server with their status. I try to pull the token out of the keychain, the same way I do for every other network call, and update the status. But for some users, the value is nil. Without it, I can't update the network stuff. Why would this work for most, but not for a small percentage?
KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"MyCustomIdentifier" accessGroup:nil]; NSString *token = [wrapper objectForKey:(__bridge id)kSecValueData];
I've gone back to the non-ARC version of the keychainwrapper, but I still get the same results. I would appreciate any feedback on this. It is only a small part of my users, but it is an issue I would like to fix and not worry about. Thanks in advance.
Also, all of my background work is set up in a backgroundTask to prevent things from timing out. I'm not having any issues with the work surrounding the keychain, but I don't let things go forward until my token is filled.
EDIT I've figured out my issue with they keychain not retrieving values from the background. I will post the answer below and accept it as I feel this question may become valuable to others later.
Choose Apple menu > System Preferences. Click iCloud, then select Keychain. When asked to enter your iCloud Security Code, click Forgot Code. Click Reset Keychain to replace your iCloud Keychain in the cloud with the iCloud Keychain items on your Mac.
Go to Keychain Access in the menu-bar and click Preferences. 6. Select "Reset My Default Keychain".
Keychain: The keychain is a secure and encrypted storage place for sensitive data. You can think of it is a database of sensitive information. Keychain Item: This is a registry in the keychain. Item Class: You can think of a class as a template of information you want to store.
My question was close to the mark for the reason why, but not quite. After reading through blog after blog, tutorial after tutorial, I finally found one that gave off a hint of what might be happening.
Locked home screens. The keychain tutorials always left the accessibility settings for the keychain blank, so it would default to Apple's lowest/safest access level. This level however doesn't allow keychain access if the user has a passcode on the lock screen. Bingo! This explains the sporadic behavior and why this only happens to a small percentage of users.
One line of code, solves the entire mess.
[wrapper setObject:(__bridge id)kSecAttrAccessibleAlways forKey:(__bridge id)kSecAttrAccessible];
Add this line where I'm setting the username and password values. Works like a charm. Hope this will help someone out there. It confounded me for quite a while until I was able to put the pieces together.
Use kSecAttrAccessibleAfterFirstUnlock
instead of kSecAttrAccessibleAlways
.
From Apple's documentation:
kSecAttrAccessibleAfterFirstUnlock
The data in the keychain item cannot be accessed after a restart until the device has been unlocked once by the user.After the first unlock, the data remains accessible until the next restart. This is recommended for items that need to be accessed by background applications. Items with this attribute migrate to a new device when using encrypted backups.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With