iOS 9 Swift Programming Cookbook (2015)
Chapter 11. Security
11.4 Authenticating the User with Touch ID and Timeout
Problem
You want to ask the user for permission to read secure content in the keychain. This includes setting a timeout after which you will no longer have access.
Solution
Follow these steps:
1. Create your access control flags with SecAccessControlCreateWithFlags, as you saw in Recipe 11.2.
2. Instantiate a context object of type LAContext.
3. Set the touchIDAuthenticationAllowableReuseDuration property of your context to LATouchIDAuthenticationMaximumAllowableReuseDuration, so your context will lock out only after the maximum allowed number of seconds.
4. Call the evaluateAccessControl(_:operation:localizedReason:) method on your context to get access to the access control.
5. If you gain access, create your keychain request dictionary and include the kSecUseAuthenticationContext key. The value of this key will be your context object.
6. Use the SecItemCopyMatching function with your dictionary to read a secure object with the given access controls.
Discussion
Whenever you write an item to the keychain, you can do so with the access controls as we saw in Recipe 11.2. So assume that your item requires Touch ID. If you want to read that item now, you need to request permission to do so. Let’s define our context and the reason why want to read the item:
let context = LAContext()
let reason = "To unlock previously stored security phrase"
Then define your access controls as before:
guard let flags =
SecAccessControlCreateWithFlags(kCFAllocatorDefault,
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
SecAccessControlCreateFlags.TouchIDAny, nil) else{
print("Could not create the access control flags")
return
}
Also specify how long you can get access. After this time passes, the user will be forced to use Touch ID again to unlock the context:
context.touchIDAuthenticationAllowableReuseDuration =
LATouchIDAuthenticationMaximumAllowableReuseDuration
Last but not least, gain access to the given access controls and read the item if possible:
context.evaluateAccessControl(flags,
operation: LAAccessControlOperation.UseItem,
localizedReason: reason) {[unowned context] succ, err in
guard succ && err == nil else {
print("Could not evaluate the access control")
if let e = err {
print("Error = \(e)")
}
return
}
print("Successfully evaluated the access control")
let service = "onlinePasswords"
let attrs = [
kSecClass.str() : kSecClassGenericPassword.str(),
kSecAttrService.str() : service,
kSecUseAuthenticationUI.str() : kSecUseAuthenticationUIAllow.str(),
kSecAttrAccessControl.str() : flags,
kSecReturnData.str() : kCFBooleanTrue,
kSecUseAuthenticationContext.str() : context,
]
//now attempt to use the attrs with SecItemCopyMatching
print(attrs)
}
The operation argument of the evaluateAccessControl(_:operation:localizedReason:) method takes in a value of type LAAccessControlOperation that indicate the type of operation you want to perform. Some of the values that you can use are UseItem, CreateItem,CreateKey, and UseKeySign.
See Also