{ "id": "165733", "key": "TIMOB-24368", "fields": { "issuetype": { "id": "1", "description": "A problem which impairs or prevents the functions of the product.", "name": "Bug", "subtask": false }, "project": { "id": "10153", "key": "TIMOB", "name": "Titanium SDK/CLI", "projectCategory": { "id": "10100", "description": "Titanium and related SDKs used in application development", "name": "Client" } }, "fixVersions": [ { "id": "18414", "description": "", "name": "Release 6.2.0", "archived": false, "released": true, "releaseDate": "2017-09-13" } ], "resolution": { "id": "1", "description": "A fix for this issue is checked into the tree and tested.", "name": "Fixed" }, "resolutiondate": "2017-08-11T21:24:21.000+0000", "created": "2017-02-02T15:58:41.000+0000", "priority": { "name": "Critical", "id": "1" }, "labels": [ "ios", "keychain-access", "keychains", "touchid" ], "versions": [], "issuelinks": [], "assignee": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "updated": "2017-08-14T21:19:22.000+0000", "status": { "description": "The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.", "name": "Closed", "id": "6", "statusCategory": { "id": 3, "key": "done", "colorName": "green", "name": "Done" } }, "components": [ { "id": "10206", "name": "iOS", "description": "iOS Platform" } ], "description": "I believe I just found some irregularities with this module. I've tried using it this way:\r\n \r\n{code:java}\r\nvar keychainItem = TouchID.createKeychainItem({\r\n identifier: \"mypassword\",\r\n accessibilityMode: TouchID.ACCESSIBLE_AFTER_FIRST_UNLOCK,\r\n});\r\n\t\r\nkeychainItem.addEventListener(\"save\", function(e) {\r\n if (!e.success) {\r\n alert(\"Error saving to the keychain: \" + e.error);\r\n return;\r\n }\r\n\t\r\n alert(\"Successfully saved!\" + e);\r\n});\r\n\t\r\nbtnSave.addEventListener(\"click\", function() {\r\n keychainItem.save(\"s3cr3t_p4$$w0rd\");\r\n});\r\n{code}\r\n\r\nWhen 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.\r\nAccording 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.\r\nPlease reopen the ticket.", "attachment": [], "flagged": false, "summary": "Ti.TouchID module accessibilityMode constants not working as expected", "creator": { "name": "nradaev", "key": "nradaev", "displayName": "Nikita Radaev", "active": true, "timeZone": "America/Los_Angeles" }, "subtasks": [], "reporter": { "name": "nradaev", "key": "nradaev", "displayName": "Nikita Radaev", "active": true, "timeZone": "America/Los_Angeles" }, "environment": "Ti.TouchID module v.2.0.1 \r\nTi.SDK 6.0.1 GA\r\nLatest studio,\r\nLatest CLI,\r\niPhone 7 os.v 10.2 ", "comment": { "comments": [ { "id": "406158", "author": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "body": "Hey [~nradaev],\r\n\r\ndoes this test-case match your issue:\r\n{code:js}\r\nvar TouchID = require(\"ti.touchid\");\r\n\r\nvar win = Ti.UI.createWindow();\r\n\r\nvar btn1 = Ti.UI.createButton({\r\n title: 'save',\r\n top: 40\r\n});\r\n\r\nvar btn2 = Ti.UI.createButton({\r\n title: 'read',\r\n top: 80\r\n});\r\n\r\nvar keychainItem = TouchID.createKeychainItem({\r\n identifier: \"mypassword\",\r\n accessibilityMode: TouchID.ACCESSIBLE_AFTER_FIRST_UNLOCK\r\n});\r\n\r\nkeychainItem.addEventListener(\"save\", function(e) {\r\n if (!e.success) {\r\n Ti.API.error(\"Error saving to the keychain: \" + e.error);\r\n return;\r\n }\r\n\r\n alert(\"Successfully saved!\" + e);\r\n});\r\n\r\nkeychainItem.addEventListener(\"read\", function(e) {\r\n if (!e.success) {\r\n Ti.API.error(\"Error reading from the keychain: \" + e.error);\r\n return;\r\n }\r\n Ti.API.info(e);\r\n});\r\n\r\nbtn1.addEventListener('click', function() {\r\n keychainItem.save(\"s3cr3t_p4$$w0rd\");\r\n});\r\n\r\nbtn2.addEventListener('click', function() {\r\n keychainItem.read();\r\n});\r\n\r\nwin.add(btn1);\r\nwin.add(btn2);\r\nwin.open();\r\n{code}\r\nI'm preparing it already.", "updateAuthor": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "created": "2017-02-02T16:09:24.000+0000", "updated": "2017-02-03T15:34:14.000+0000" }, { "id": "406278", "author": { "name": "nradaev", "key": "nradaev", "displayName": "Nikita Radaev", "active": true, "timeZone": "America/Los_Angeles" }, "body": "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)", "updateAuthor": { "name": "nradaev", "key": "nradaev", "displayName": "Nikita Radaev", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2017-02-03T15:22:27.000+0000", "updated": "2017-02-03T15:22:27.000+0000" }, { "id": "406280", "author": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "body": "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.\r\n\r\n*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 {{kSecAttrAccessibleAfterFirstUnlock}} are used to ensure data protection in background-modes. To control when to show the auth dialog, you need to set one of the {{ACCESS_CONTROL_\\*}} constants, the default one is {{ACCESS_CONTROL_USER_PRESENCE}} which *will prompt*. And also note that the {{accessGroup}} property is also used when the {{accessibilityMode}} property is set, since they are depending on each other.\r\n\r\nI'll need to check a native example again.", "updateAuthor": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "created": "2017-02-03T15:46:26.000+0000", "updated": "2017-02-03T16:04:05.000+0000" }, { "id": "406282", "author": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "body": "Ok, here is the updated example along with a PR.\r\n\r\nPR: https://github.com/appcelerator-modules/ti.touchid/pull/26\r\nModule 2.1.1: https://github.com/appcelerator-modules/ti.touchid/files/750982/ti.touchid-iphone-2.1.1.zip\r\n\r\nDemo:\r\n{code:js}\r\nvar TouchID = require(\"ti.touchid\");\r\n\r\nvar win = Ti.UI.createWindow();\r\n\r\nvar btn1 = Ti.UI.createButton({\r\n title: 'save',\r\n top: 40\r\n});\r\n\r\nvar btn2 = Ti.UI.createButton({\r\n title: 'read',\r\n top: 80\r\n});\r\n\r\nvar btn3 = Ti.UI.createButton({\r\n title: 'delete',\r\n top: 120\r\n});\r\n\r\n\r\nvar keychainItem = TouchID.createKeychainItem({\r\n identifier: \"mypassword\",\r\n accessibilityMode: TouchID.ACCESSIBLE_AFTER_FIRST_UNLOCK,\r\n accessControlMode: TouchID.ACCESS_CONTROL_TOUCH_ID_ANY\r\n});\r\n\r\nkeychainItem.addEventListener(\"save\", function(e) {\r\n if (!e.success) {\r\n Ti.API.error(\"Error saving to the keychain: \" + e.error);\r\n return;\r\n }\r\n\r\n alert(\"Successfully saved!\" + e);\r\n});\r\n\r\nkeychainItem.addEventListener(\"read\", function(e) {\r\n if (!e.success) {\r\n Ti.API.error(\"Error reading from the keychain: \" + e.error);\r\n return;\r\n }\r\n Ti.API.info(e);\r\n});\r\n\r\nkeychainItem.addEventListener(\"reset\", function(e) {\r\n if (!e.success) {\r\n Ti.API.error(\"Error deleting from the keychain: \" + e.error);\r\n return;\r\n }\r\n Ti.API.info(e);\r\n});\r\n\r\nbtn1.addEventListener('click', function() {\r\n keychainItem.save(\"s3cr3t_p4$$w0rd\");\r\n});\r\n\r\nbtn2.addEventListener('click', function() {\r\n keychainItem.read();\r\n});\r\n\r\nbtn3.addEventListener('click', function() {\r\n keychainItem.reset();\r\n});\r\n\r\nwin.add(btn1);\r\nwin.add(btn2);\r\nwin.add(btn3);\r\nwin.open();\r\n{code}\r\nBefore, there was a default value \"kSecAccessControlUserPresence\" causing the auth dialog to always show. Now we're passing {{NULL}} if no {{accessControlMode}} is set. In the above example, it will prompt for the auth dialog, but if you remove the {{accessControlMode}} property, it won't (by default). Also, changing it to something else from the {{ACCESS_CONTROL_\\*}} namespace will change the behavior as documented. Please try it out!", "updateAuthor": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "created": "2017-02-03T16:19:47.000+0000", "updated": "2017-02-03T16:21:40.000+0000" }, { "id": "406434", "author": { "name": "nradaev", "key": "nradaev", "displayName": "Nikita Radaev", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Hi Hans,\r\n\r\nSorry 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.", "updateAuthor": { "name": "nradaev", "key": "nradaev", "displayName": "Nikita Radaev", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2017-02-06T21:59:26.000+0000", "updated": "2017-02-06T21:59:26.000+0000" }, { "id": "406534", "author": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "body": "[~nradaev] Can you confirm the final test? I'd move it to review then :-).", "updateAuthor": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "created": "2017-02-07T20:18:58.000+0000", "updated": "2017-02-07T20:18:58.000+0000" }, { "id": "406539", "author": { "name": "nradaev", "key": "nradaev", "displayName": "Nikita Radaev", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Hi Hans, \r\n\r\nTouchID.ACCESSIBLE_WHEN_UNLOCKED doesnt seem to work as described. I was able to retrieve keychain item when device was locked. \r\nTouchID.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. \r\n\r\nJust a small note - looks like TouchID.ACCESSIBLE_ALWAYS has been deprecated in iOS9, Thought it was worth mentioning...\r\n\r\nEverything else seems to be in order.\r\n", "updateAuthor": { "name": "nradaev", "key": "nradaev", "displayName": "Nikita Radaev", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2017-02-07T21:46:08.000+0000", "updated": "2017-02-07T21:46:08.000+0000" }, { "id": "422044", "author": { "name": "nradaev", "key": "nradaev", "displayName": "Nikita Radaev", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Hi Hans,\r\n\r\nFound another irregularity with TouchID module v2.1.2 - deviceCanAuthenticate() does not seem to return correct error codes when passcode is not provided.\r\n\r\nHere is my test code:\r\n\r\n\r\n{code:java}\r\nvar TouchID = require(\"ti.touchid\");\r\nvar res = TouchID.deviceCanAuthenticate();\r\nTi.API.info(res);\r\n{code}\r\n\r\n\r\nTest scenario:\r\n\r\n1) User has passcode setup but no fingerprints.\r\n2) User has fingerprints setup but passcode has been turned off.\r\n\r\nBoth scenarios produce same output: \r\n\r\n\r\n{code:java}\r\n[INFO] : {\r\n[INFO] : canAuthenticate = 0;\r\n[INFO] : code = \"-7\";\r\n[INFO] : error = \"No fingers are enrolled with Touch ID.\";\r\n[INFO] : }\r\n{code}\r\n\r\n\r\nIt would be great if we could have something like TouchID.hasFingerprints() and TouchId.hasPasscode() methods.\r\n\r\nThank you, \r\n\r\nNikita.\r\n", "updateAuthor": { "name": "nradaev", "key": "nradaev", "displayName": "Nikita Radaev", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2017-06-15T16:06:31.000+0000", "updated": "2017-06-15T16:06:59.000+0000" }, { "id": "422150", "author": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "body": "Hey there!\r\n\r\nThis 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 {{authenticationPolicy}} to specify the policy used to authenticate and validate the authentication-status. Example:\r\n{code:js\r\n// One of `AUTHENTICATION_POLICY_PASSCODE` or `AUTHENTICATION_POLICY_BIOMETRICS ` (default)\r\nTouchID.setAuthenticationPolicy(TouchID.AUTHENTICATION_POLICY_PASSCODE);\r\n{code}\r\nSetting that before, {{deviceCanAuthenticate}} will evaluate that policy internally. Give it a try.", "updateAuthor": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "created": "2017-06-16T12:28:40.000+0000", "updated": "2017-06-16T12:28:40.000+0000" }, { "id": "422155", "author": { "name": "nradaev", "key": "nradaev", "displayName": "Nikita Radaev", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Hi Hans, \r\n\r\nThank you. Works exactly as described. Looks like I gotta read API doc more closely next time.\r\n\r\nNikita.", "updateAuthor": { "name": "nradaev", "key": "nradaev", "displayName": "Nikita Radaev", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2017-06-16T14:42:08.000+0000", "updated": "2017-06-16T14:42:08.000+0000" }, { "id": "422565", "author": { "name": "nradaev", "key": "nradaev", "displayName": "Nikita Radaev", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Hi,\r\n\r\nI'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):\r\n\r\nACCESSIBLE_ALWAYS, \r\nACCESSIBLE_ALWAYS_THIS_DEVICE_ONLY ,\r\nACCESSIBLE_WHEN_UNLOCKED, \r\nACCESSIBLE_WHEN_UNLOCKED_THIS_DEVICE_ONLY, \r\nACCESSIBLE_AFTER_FIRST_UNLOCK,\r\nACCESSIBLE_AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY,\r\n\r\nLinks to module code where accessControlMode is being set: \r\n\r\nhttps://github.com/appcelerator-modules/ti.touchid/blob/master/ios/Classes/TiTouchidKeychainItemProxy.m#L171\r\nhttps://github.com/appcelerator-modules/ti.touchid/blob/master/ios/Classes/KeychainItemWrapper/APSKeychainWrapper.m#L202\r\n\r\nPlease provide a fix. \r\n\r\nThank you.", "updateAuthor": { "name": "nradaev", "key": "nradaev", "displayName": "Nikita Radaev", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2017-06-22T20:04:02.000+0000", "updated": "2017-06-22T20:05:46.000+0000" }, { "id": "426575", "author": { "name": "ewieber", "key": "ewieber", "displayName": "Eric Wieber", "active": false, "timeZone": "America/Los_Angeles" }, "body": "FR Passed, using:\r\nMacOS 10.12.6 (16G24b)\r\nStudio 4.9.0.201705302345\r\nTi SDK 6.1.2.GA\r\nAppc NPM 4.2.9\r\nAppc CLI 6.2.3\r\nAlloy 1.9.13\r\nXcode 8.3.3 (8E3004b)\r\n\r\nTested using the provided sample code as well as customer tests and the touchID suite/past tests", "updateAuthor": { "name": "ewieber", "key": "ewieber", "displayName": "Eric Wieber", "active": false, "timeZone": "America/Los_Angeles" }, "created": "2017-08-11T21:24:21.000+0000", "updated": "2017-08-11T21:24:21.000+0000" } ], "maxResults": 13, "total": 13, "startAt": 0 } } }