[TIMOB-24368] Ti.TouchID module accessibilityMode constants not working as expected
| GitHub Issue | n/a |
|---|---|
| Type | Bug |
| Priority | Critical |
| Status | Closed |
| Resolution | Fixed |
| Resolution Date | 2017-08-11T21:24:21.000+0000 |
| Affected Version/s | n/a |
| Fix Version/s | Release 6.2.0 |
| Components | iOS |
| Labels | ios, keychain-access, keychains, touchid |
| Reporter | Nikita Radaev |
| Assignee | Hans Knöchel |
| Created | 2017-02-02T15:58:41.000+0000 |
| Updated | 2017-08-14T21:19:22.000+0000 |
Description
I believe I just found some irregularities with this module. I've tried using it this way:
var keychainItem = TouchID.createKeychainItem({
identifier: "mypassword",
accessibilityMode: TouchID.ACCESSIBLE_AFTER_FIRST_UNLOCK,
});
keychainItem.addEventListener("save", function(e) {
if (!e.success) {
alert("Error saving to the keychain: " + e.error);
return;
}
alert("Successfully saved!" + e);
});
btnSave.addEventListener("click", function() {
keychainItem.save("s3cr3t_p4$$w0rd");
});
When I try to read the value back I am consistently being asked for password. Same goes for ACCESSIBLE_ALWAYS, ACCESSIBLE_WHEN_UNLOCKED, ACCESSIBLE_WHEN_UNLOCKED_THIS_DEVICE_ONLY, ACCESSIBLE_ALWAYS_THIS_DEVICE_ONLY.
According to https://developer.apple.com/reference/security/keychain_services/keychain_item_accessibility_constants I should not be asked the password... Tested with TouchID module v 2.1.0 Ti.SDK 6.0.1 GA on iPhone 7 os 10.2.
Please reopen the ticket.
Hey [~nradaev], does this test-case match your issue:
I'm preparing it already.var TouchID = require("ti.touchid"); var win = Ti.UI.createWindow(); var btn1 = Ti.UI.createButton({ title: 'save', top: 40 }); var btn2 = Ti.UI.createButton({ title: 'read', top: 80 }); var keychainItem = TouchID.createKeychainItem({ identifier: "mypassword", accessibilityMode: TouchID.ACCESSIBLE_AFTER_FIRST_UNLOCK }); keychainItem.addEventListener("save", function(e) { if (!e.success) { Ti.API.error("Error saving to the keychain: " + e.error); return; } alert("Successfully saved!" + e); }); keychainItem.addEventListener("read", function(e) { if (!e.success) { Ti.API.error("Error reading from the keychain: " + e.error); return; } Ti.API.info(e); }); btn1.addEventListener('click', function() { keychainItem.save("s3cr3t_p4$$w0rd"); }); btn2.addEventListener('click', function() { keychainItem.read(); }); win.add(btn1); win.add(btn2); win.open();looks correct. I did some more testing yesterday and it looks like if you dont pass accessibilityMode at all, it falls back to ACCESSIBLE_AFTER_FIRST_UNLOCK (I was able to read keychain item without having to enter password)
If you don't pass anything *in a clean keychain item*, then it will ignore the accessibility mode [here](https://github.com/appcelerator-modules/ti.touchid/blob/master/ios/Classes/KeychainItemWrapper/APSKeychainWrapper.m#L202). I think I have to remove [this value](https://github.com/appcelerator-modules/ti.touchid/blob/master/ios/Classes/KeychainItemWrapper/APSKeychainWrapper.m#L208) to not force the UI dialog and let the accessibility mode decide. *EDIT*: [The apple docs](https://developer.apple.com/reference/security/keychain_services/keychain_item_accessibility_constants) do not state when the auth-dialog should be shown. The accessibility rather is responsible for deciding when the whole data is accessible. In this case, constants like
kSecAttrAccessibleAfterFirstUnlockare used to ensure data protection in background-modes. To control when to show the auth dialog, you need to set one of theACCESS_CONTROL_\*constants, the default one isACCESS_CONTROL_USER_PRESENCEwhich *will prompt*. And also note that theaccessGroupproperty is also used when theaccessibilityModeproperty is set, since they are depending on each other. I'll need to check a native example again.Ok, here is the updated example along with a PR. PR: https://github.com/appcelerator-modules/ti.touchid/pull/26 Module 2.1.1: https://github.com/appcelerator-modules/ti.touchid/files/750982/ti.touchid-iphone-2.1.1.zip Demo:
Before, there was a default value "kSecAccessControlUserPresence" causing the auth dialog to always show. Now we're passingvar TouchID = require("ti.touchid"); var win = Ti.UI.createWindow(); var btn1 = Ti.UI.createButton({ title: 'save', top: 40 }); var btn2 = Ti.UI.createButton({ title: 'read', top: 80 }); var btn3 = Ti.UI.createButton({ title: 'delete', top: 120 }); var keychainItem = TouchID.createKeychainItem({ identifier: "mypassword", accessibilityMode: TouchID.ACCESSIBLE_AFTER_FIRST_UNLOCK, accessControlMode: TouchID.ACCESS_CONTROL_TOUCH_ID_ANY }); keychainItem.addEventListener("save", function(e) { if (!e.success) { Ti.API.error("Error saving to the keychain: " + e.error); return; } alert("Successfully saved!" + e); }); keychainItem.addEventListener("read", function(e) { if (!e.success) { Ti.API.error("Error reading from the keychain: " + e.error); return; } Ti.API.info(e); }); keychainItem.addEventListener("reset", function(e) { if (!e.success) { Ti.API.error("Error deleting from the keychain: " + e.error); return; } Ti.API.info(e); }); btn1.addEventListener('click', function() { keychainItem.save("s3cr3t_p4$$w0rd"); }); btn2.addEventListener('click', function() { keychainItem.read(); }); btn3.addEventListener('click', function() { keychainItem.reset(); }); win.add(btn1); win.add(btn2); win.add(btn3); win.open();NULLif noaccessControlModeis set. In the above example, it will prompt for the auth dialog, but if you remove theaccessControlModeproperty, it won't (by default). Also, changing it to something else from theACCESS_CONTROL_\*namespace will change the behavior as documented. Please try it out!Hi Hans, Sorry for taking my sweet time with testing this. I've done some preliminary testing today - so far so good. I will finish up tomorrow and provide an update. Thanks for quick turnaround on this.
[~nradaev] Can you confirm the final test? I'd move it to review then :-).
Hi Hans, TouchID.ACCESSIBLE_WHEN_UNLOCKED doesnt seem to work as described. I was able to retrieve keychain item when device was locked. TouchID.ACCESSIBLE_WHEN_PASSCODE_SET_THIS_DEVICE_ONLY is invalidated once passcode is removed. However, when passcode is turned back on, keychain item is still inaccessible. Not sure if this behavior is by design. Just a small note - looks like TouchID.ACCESSIBLE_ALWAYS has been deprecated in iOS9, Thought it was worth mentioning... Everything else seems to be in order.
Hi Hans, Found another irregularity with TouchID module v2.1.2 - deviceCanAuthenticate() does not seem to return correct error codes when passcode is not provided. Here is my test code:
Test scenario: 1) User has passcode setup but no fingerprints. 2) User has fingerprints setup but passcode has been turned off. Both scenarios produce same output:var TouchID = require("ti.touchid"); var res = TouchID.deviceCanAuthenticate(); Ti.API.info(res);It would be great if we could have something like TouchID.hasFingerprints() and TouchId.hasPasscode() methods. Thank you, Nikita.[INFO] : { [INFO] : canAuthenticate = 0; [INFO] : code = "-7"; [INFO] : error = "No fingers are enrolled with Touch ID."; [INFO] : }Hey there! This is a whole different issue and should not be discussed in this ticket, but here is the solution: Since Ti.TouchID 2.1.0, you can set the
authenticationPolicyto specify the policy used to authenticate and validate the authentication-status. Example: {code:js // One ofAUTHENTICATION_POLICY_PASSCODEorAUTHENTICATION_POLICY_BIOMETRICS(default) TouchID.setAuthenticationPolicy(TouchID.AUTHENTICATION_POLICY_PASSCODE); ``Setting that before,deviceCanAuthenticate` will evaluate that policy internally. Give it a try.Hi Hans, Thank you. Works exactly as described. Looks like I gotta read API doc more closely next time. Nikita.
Hi, I've done some additional testing and got to the root of the problem identified in the ticket. It appears that accessControlMode always falls back to USER_PRESENSE even though its a non-required value making these constants completely useless (device always requires passcode or fingeprint): ACCESSIBLE_ALWAYS, ACCESSIBLE_ALWAYS_THIS_DEVICE_ONLY , ACCESSIBLE_WHEN_UNLOCKED, ACCESSIBLE_WHEN_UNLOCKED_THIS_DEVICE_ONLY, ACCESSIBLE_AFTER_FIRST_UNLOCK, ACCESSIBLE_AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY, Links to module code where accessControlMode is being set: https://github.com/appcelerator-modules/ti.touchid/blob/master/ios/Classes/TiTouchidKeychainItemProxy.m#L171 https://github.com/appcelerator-modules/ti.touchid/blob/master/ios/Classes/KeychainItemWrapper/APSKeychainWrapper.m#L202 Please provide a fix. Thank you.
FR Passed, using: MacOS 10.12.6 (16G24b) Studio 4.9.0.201705302345 Ti SDK 6.1.2.GA Appc NPM 4.2.9 Appc CLI 6.2.3 Alloy 1.9.13 Xcode 8.3.3 (8E3004b) Tested using the provided sample code as well as customer tests and the touchID suite/past tests