{ "id": "155609", "key": "TIMOB-20547", "fields": { "issuetype": { "id": "2", "description": "A new feature of the product, which has yet to be developed.", "name": "New Feature", "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": "17608", "name": "Release 6.1.0", "archived": false, "released": true, "releaseDate": "2017-05-26" } ], "resolution": { "id": "1", "description": "A fix for this issue is checked into the tree and tested.", "name": "Fixed" }, "resolutiondate": "2017-01-11T04:25:30.000+0000", "created": "2016-03-10T19:22:46.000+0000", "priority": { "name": "Medium", "id": "3" }, "labels": [], "versions": [], "issuelinks": [ { "id": "53320", "type": { "id": "10003", "name": "Relates", "inward": "relates to", "outward": "relates to" }, "outwardIssue": { "id": "164344", "key": "TIMOB-24094", "fields": { "summary": "Ti.TouchID module timeout extension", "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" } }, "priority": { "name": "None", "id": "6" }, "issuetype": { "id": "4", "description": "An improvement or enhancement to an existing feature or task.", "name": "Improvement", "subtask": false } } } } ], "assignee": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "updated": "2017-02-02T15:23:45.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": "Customers are unable to secure a key-value pair in the Keychain and then unlock it using Touch Id or passcode. \r\n\r\nHere is a talk by Apple at WWDC 2014 that describes what users are looking to do. \r\nhttps://developer.apple.com/videos/play/wwdc2014/711/", "attachment": [], "flagged": false, "summary": "iOS: Add Keychain-access to Ti.TouchID, add passcode-fallback mode, expose new iOS 10 TouchID API's", "creator": { "name": "rramirez", "key": "rramirez", "displayName": " Ricardo Ramirez", "active": true, "timeZone": "America/Los_Angeles" }, "subtasks": [], "reporter": { "name": "rramirez", "key": "rramirez", "displayName": " Ricardo Ramirez", "active": true, "timeZone": "America/Los_Angeles" }, "environment": "Xcode 7.2\r\niOS 9.4", "closedSprints": [ { "id": 787, "state": "closed", "name": "2017 Sprint 01 SDK", "startDate": "2016-12-31T02:15:33.446Z", "endDate": "2017-01-14T02:15:00.000Z", "completeDate": "2017-01-15T00:00:19.006Z", "originBoardId": 114 } ], "comment": { "comments": [ { "id": "379532", "author": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "body": "We have [ti.touchid|https://github.com/appcelerator-modules/ti.touchid] for fingerprint-secured access and there is a [native module|https://github.com/pegli/ti_keychain] that supports keychain access. ", "updateAuthor": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "created": "2016-03-11T12:48:47.000+0000", "updated": "2016-03-11T12:48:47.000+0000" }, { "id": "386062", "author": { "name": "rramirez", "key": "rramirez", "displayName": " Ricardo Ramirez", "active": true, "timeZone": "America/Los_Angeles" }, "body": "[~cng] [~hansknoechel] Any update here ? \r\n\r\nThe Appcelerator ti.touchid module only uses LocalAuthentication and does not involve the KeyChain so its basically useless if you want to use it to replace a password. \r\n\r\nThe https://github.com/pegli/ti_keychain native module wraps the KeyChain but does not utilize TouchId.", "updateAuthor": { "name": "rramirez", "key": "rramirez", "displayName": " Ricardo Ramirez", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2016-05-18T16:49:38.000+0000", "updated": "2016-05-18T16:49:38.000+0000" }, { "id": "386065", "author": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "body": "[~rramirez] iOS does not expose the keychain together with the touchID-dialog (same natively). So Apple does not see that use-case, yet. Similar discussion here: https://github.com/appcelerator-modules/ti.touchid/issues/15", "updateAuthor": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "created": "2016-05-18T17:08:17.000+0000", "updated": "2016-05-18T17:08:17.000+0000" }, { "id": "386068", "author": { "name": "CollinPrice", "key": "collinprice", "displayName": "Collin Price", "active": true, "timeZone": "America/Toronto" }, "body": "Hans, this functionality was added in iOS 9. Apple created a sample project to demonstrate it.\r\n\r\nhttps://developer.apple.com/library/ios/samplecode/KeychainTouchID/Introduction/Intro.html", "updateAuthor": { "name": "CollinPrice", "key": "collinprice", "displayName": "Collin Price", "active": true, "timeZone": "America/Toronto" }, "created": "2016-05-18T17:12:52.000+0000", "updated": "2016-05-18T17:12:52.000+0000" }, { "id": "386069", "author": { "name": "CollinPrice", "key": "collinprice", "displayName": "Collin Price", "active": true, "timeZone": "America/Toronto" }, "body": "Also, issue 15 on ti.touchid is completely unrelated. Replacing a password was just an example I brought up. What we are looking for is to secure a key-value pair in the keychain that is only accessible through TouchId.", "updateAuthor": { "name": "CollinPrice", "key": "collinprice", "displayName": "Collin Price", "active": true, "timeZone": "America/Toronto" }, "created": "2016-05-18T17:17:41.000+0000", "updated": "2016-05-18T17:17:41.000+0000" }, { "id": "386077", "author": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "body": "Hey Collin, thanks for the feedback! So the Github reference dealt with the ability to enter the native PIN, so thats why I called it \"similar\". I checked the example. So it is possible to save a password and in the keychain and fetch it using TouchID. Is that what you want? And how would you like to access it from the JS? As more infos the better. Thanks!", "updateAuthor": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "created": "2016-05-18T18:19:51.000+0000", "updated": "2016-05-18T18:19:51.000+0000" }, { "id": "386082", "author": { "name": "CollinPrice", "key": "collinprice", "displayName": "Collin Price", "active": true, "timeZone": "America/Toronto" }, "body": "Ultimately our ask is for iOS/Android KeyChain access. Both platforms allow data to be encrypted within a keychain at a variety of levels.\r\n\r\n*Basic*: Securely store key-value pairs in the Keychain. These values are only released once a device has been unlocked.\r\n*Enchanced*: These values are only released once a device has been unlocked with a passcode/pattern/fingerprint and the key-values are removed automatically if the passcode/pattern/fingerprint are ever removed.\r\n*Biometric*: same as Enhanced but only applies to fingerprints.\r\n\r\nIf you're looking at TouchId only an API might look like this:\r\n\r\nisTouchIdSupported() : boolean\r\nisTouchIdEnabled() : boolean\r\nsaveWithTouchId(key, value) : boolean\r\nreadWithTouchId(key, callback) : void // where callback returns a success or error\r\n\r\nAndroid M (SDK 23) has introduced similar support for fingerprint access and keychain integration so a cross-platform module could be created.", "updateAuthor": { "name": "CollinPrice", "key": "collinprice", "displayName": "Collin Price", "active": true, "timeZone": "America/Toronto" }, "created": "2016-05-18T18:41:23.000+0000", "updated": "2016-05-18T18:41:23.000+0000" }, { "id": "391114", "author": { "name": "ingo", "key": "ingo", "displayName": "Ingo Muschenetz", "active": true, "timeZone": "America/Los_Angeles" }, "body": "[~hansknoechel] Can we use Hyperloop here?", "updateAuthor": { "name": "ingo", "key": "ingo", "displayName": "Ingo Muschenetz", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2016-07-20T19:02:44.000+0000", "updated": "2016-07-20T19:02:44.000+0000" }, { "id": "394650", "author": { "name": "rramirez", "key": "rramirez", "displayName": " Ricardo Ramirez", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Any update ? ", "updateAuthor": { "name": "rramirez", "key": "rramirez", "displayName": " Ricardo Ramirez", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2016-08-29T17:02:03.000+0000", "updated": "2016-08-29T17:02:03.000+0000" }, { "id": "395224", "author": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "body": "Ok, so the primary request is to enable the key-value storage and protect it with a fingerprint. Since all keychain-operations need to be done on a C-level, Apple provides an (official) [Objective-C wrapper|https://developer.apple.com/library/ios/samplecode/GenericKeychain/Listings/Classes_KeychainItemWrapper_h.html] we could use to wrap it. (*EDIT*: See the full demo below)\r\n\r\n[~CollinPrice] Is that something you could work with? Of course the PR is not ready for production, yet and is awaiting feedback on the public interface-proposal. :-)", "updateAuthor": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "created": "2016-09-02T19:51:44.000+0000", "updated": "2016-11-03T11:58:46.000+0000" }, { "id": "395269", "author": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "body": "After thinking it a bit more, I would not add the {{authorizationRequired}} to that API, since it would no the same as we already expose with the {{authenticate}} method. So chaining that one before calling the above method to save / access data would make more sense to me. Also, looking into the sample app that Apple provides for keychain access, there is a special dialog to save passwords to the keychain by typing and confirming it in a system-dialog. \r\n\r\nWe could also expose that one to {{addPasswordToKeychain}} if needed. Please let me know what exactly is required in the first step as well as future steps for additional implementations. Thanks!", "updateAuthor": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "created": "2016-09-03T14:11:07.000+0000", "updated": "2016-09-03T14:11:07.000+0000" }, { "id": "395318", "author": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "body": "PR: https://github.com/appcelerator-modules/ti.touchid/pull/23\r\nNew version (1.3.0): [Download|https://www.dropbox.com/s/ro3lknh5v7j2rqy/ti.touchid-iphone-1.3.0.zip?dl=0]\r\n\r\nThe above PR adds support for writing, reading and deleting values from the native iOS-keychain. As discussed about, I removed the {{authorizationRequired}} from the params list to keep the keychain clean. Please let me know if you need additional fields to be covered.\r\n\r\n*EDIT*: I will add docs and put this in review as soon as the public interface is approved. \r\n\r\nDemo:\r\n{code:javascript}\r\nvar win = Ti.UI.createWindow({\r\n backgroundColor : \"#fff\",\r\n layout: \"vertical\"\r\n}); \r\n\r\nvar btnSave = Ti.UI.createButton({\r\n title : \"Save password to keychain\",\r\n top: 40\r\n});\r\n\r\nbtnSave.addEventListener(\"click\", function() {\r\n var TouchID = require(\"ti.touchid\");\r\n \r\n TouchID.saveValueToKeychain({\r\n identifier: \"password\",\r\n accessGroup: \"com.appcelerator\",\r\n value: \"s3cr3t_p4$$w0rd\",\r\n success: function(e) {\r\n Ti.API.info(\"Success!\");\r\n Ti.API.info(e);\r\n },\r\n error: function(e) {\r\n Ti.API.error(\"Error!\");\r\n Ti.API.error(e);\r\n }\r\n });\r\n});\r\n\r\nvar btnRead = Ti.UI.createButton({\r\n title : \"Read password from keychain\",\r\n top: 40\r\n});\r\n\r\nbtnRead.addEventListener(\"click\", function() {\r\n var TouchID = require(\"ti.touchid\");\r\n \r\n TouchID.readValueFromKeychain({\r\n identifier: \"password\",\r\n accessGroup: \"com.appcelerator\",\r\n success: function(e) {\r\n Ti.API.info(\"Success!\");\r\n Ti.API.info(e);\r\n },\r\n error: function(e) {\r\n Ti.API.error(\"Error!\");\r\n Ti.API.error(e);\r\n }\r\n });\r\n});\r\n\r\n\r\nvar btnDelete = Ti.UI.createButton({\r\n title : \"Delete password from keychain\",\r\n top: 40\r\n});\r\n\r\nbtnDelete.addEventListener(\"click\", function() {\r\n var TouchID = require(\"ti.touchid\");\r\n \r\n TouchID.deleteValueFromKeychain({\r\n identifier: \"password\",\r\n accessGroup: \"com.appcelerator\"\r\n });\r\n});\r\n\r\nwin.add(btnSave);\r\nwin.add(btnRead);\r\nwin.add(btnDelete);\r\nwin.open();\r\n{code}", "updateAuthor": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "created": "2016-09-05T08:00:52.000+0000", "updated": "2016-09-05T08:05:03.000+0000" }, { "id": "400327", "author": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "body": "The PR is ready to review now! I'll update the exmaple above and add another example that covers the new TouchID features that I also added.", "updateAuthor": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "created": "2016-11-03T11:06:06.000+0000", "updated": "2016-11-03T11:06:06.000+0000" }, { "id": "400335", "author": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "body": "Ok, so I kind of escalated here (in a good way :-). We now support the following new features:\r\n- Save, read and delete values with the native iOS keychain\r\n- Expose the properties {{maxBiometryFailures}}, {{allowableReuseDuration}}, {{fallbackTitle}} and {{cancelTitle}} to customize the Touch ID experience\r\n- Expose the {{invalidate()}} method to hide the Touch ID dialog programatically \r\n- Major refactoring in both code-base and documentation\r\n- Align module versions to match 2.1.0\r\n\r\nPackaged module: https://github.com/appcelerator-modules/ti.touchid/releases/tag/2.1.0-beta.1\r\n\r\nTest-Case 1: Write, read and delete from the keychain\r\n{code:javascript}\r\nvar win = Ti.UI.createWindow({\r\n backgroundColor : \"#fff\",\r\n layout: \"vertical\"\r\n}); \r\n \r\nvar btnSave = Ti.UI.createButton({\r\n title : \"Save password to keychain\",\r\n top: 40\r\n});\r\n \r\nbtnSave.addEventListener(\"click\", function() {\r\n var TouchID = require(\"ti.touchid\");\r\n \r\n TouchID.saveValueToKeychain({\r\n identifier: \"password\",\r\n accessGroup: \"com.appcelerator\",\r\n value: \"s3cr3t_p4$$w0rd\",\r\n callback: function(e) {\r\n if (!e.success) {\r\n Ti.API.error(\"Error! Please check the logs for the exact error\");\r\n return;\r\n }\r\n Ti.API.info(\"Success!\");\r\n Ti.API.info(e);\r\n },\r\n });\r\n});\r\n \r\nvar btnRead = Ti.UI.createButton({\r\n title : \"Read password from keychain\",\r\n top: 40\r\n});\r\n \r\nbtnRead.addEventListener(\"click\", function() {\r\n var TouchID = require(\"ti.touchid\");\r\n \r\n TouchID.readValueFromKeychain({\r\n identifier: \"password\",\r\n accessGroup: \"com.appcelerator\",\r\n callback: function(e) {\r\n if (!e.success) {\r\n Ti.API.error(\"Error! \" + e.error);\r\n return;\r\n }\r\n Ti.API.info(\"Success!\");\r\n Ti.API.info(e);\r\n },\r\n });\r\n});\r\n \r\n \r\nvar btnDelete = Ti.UI.createButton({\r\n title : \"Delete password from keychain\",\r\n top: 40\r\n});\r\n \r\nbtnDelete.addEventListener(\"click\", function() {\r\n var TouchID = require(\"ti.touchid\");\r\n \r\n TouchID.deleteValueFromKeychain({\r\n identifier: \"password\",\r\n accessGroup: \"com.appcelerator\"\r\n });\r\n});\r\n \r\nwin.add(btnSave);\r\nwin.add(btnRead);\r\nwin.add(btnDelete);\r\nwin.open();\r\n{code}\r\n\r\nTest-Case 2: Expose new Touch ID properties, allow the user to invalidate the dialog manually (cc [~emerriman]):\r\n{code:javascript}\r\nvar TiTouchId = require('ti.touchid');\r\n\r\nvar win = Ti.UI.createWindow();\r\nvar btn = Ti.UI.createButton({\r\n\ttitle: 'authenticate'\r\n});\r\n\r\nwin.add(btn);\r\nwin.open();\r\n\r\nbtn.addEventListener('click', function(){\r\n\r\n\tif(!TiTouchId.isSupported()) {\r\n\t\talert(\"Touch ID is not supported on this device!\");\r\n\t\treturn;\r\n\t}\r\n\t\r\n\tTiTouchId.authenticate({\r\n\t\treason: 'We need your fingerprint to continue.',\r\n\t\tmaxBiometryFailures: 3, // iOS 9+, optional, maximal attempts: 5\r\n\t\tallowableReuseDuration: 5, // iOS 9+, optional, in seconds\r\n fallbackTitle: \"Use different auth method?\", // iOS 10+, optional\r\n cancelTitle: \"Get me outta here!\", // iOS 10+, optional\r\n\t\tcallback: function(e) {\r\n\t\t\tif (!e.success) {\r\n\t\t\t\talert('Error! Message: ' + e.error + '\\nCode: ' + e.code);\r\n\t\t\t\tswitch(e.code) {\r\n\t\t\t\t\tcase TiTouchId.ERROR_AUTHENTICATION_FAILED: Ti.API.info('Error code is TiTouchId.ERROR_AUTHENTICATION_FAILED'); break;\r\n\t\t\t\t\tcase TiTouchId.ERROR_USER_CANCEL: Ti.API.info('Error code is TiTouchId.ERROR_USER_CANCEL'); break;\r\n\t\t\t\t\tcase TiTouchId.ERROR_USER_FALLBACK: Ti.API.info('Error code is TiTouchId.ERROR_USER_FALLBACK'); break;\r\n\t\t\t\t\tcase TiTouchId.ERROR_SYSTEM_CANCEL: Ti.API.info('Error code is TiTouchId.ERROR_SYSTEM_CANCEL'); break;\r\n\t\t\t\t\tcase TiTouchId.ERROR_PASSCODE_NOT_SET: Ti.API.info('Error code is TiTouchId.ERROR_PASSCODE_NOT_SET'); break;\r\n\t\t\t\t\tcase TiTouchId.ERROR_TOUCH_ID_NOT_AVAILABLE: Ti.API.info('Error code is TiTouchId.ERROR_TOUCH_ID_NOT_AVAILABLE'); break;\r\n case TiTouchId.ERROR_TOUCH_ID_NOT_ENROLLED: Ti.API.info('Error code is TiTouchId.ERROR_TOUCH_ID_NOT_ENROLLED'); break;\r\n case TiTouchId.ERROR_TOUCH_ID_NOT_ENROLLED: Ti.API.info('Error code is TiTouchId.ERROR_TOUCH_ID_NOT_ENROLLED'); break;\r\n case TiTouchId.ERROR_APP_CANCELLED: Ti.API.info('Error code is TiTouchId.ERROR_APP_CANCELLED'); break;\r\n case TiTouchId.ERROR_INVALID_CONTEXT: Ti.API.info('Error code is TiTouchId.ERROR_INVALID_CONTEXT'); break;\r\n case TiTouchId.ERROR_TOUCH_ID_LOCKOUT: Ti.API.info('Error code is TiTouchId.ERROR_TOUCH_ID_LOCKOUT'); break;\r\n default: Ti.API.info('Error code is unknown'); break;\r\n\t\t\t\t}\r\n\t\t\t} else {\r\n\t\t\t \t// do something useful\r\n\t\t\t\talert('YAY! success');\r\n\t\t\t}\r\n\t\t}\r\n\t});\r\n\t\r\n\t// When uncommented, it should invalidate (hide) after 2 seconds\r\n\tsetTimeout(function() {\r\n\t\t// TiTouchId.invalidate();\r\n\t}, 5000);\r\n});\r\n{code}\r\n", "updateAuthor": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "created": "2016-11-03T13:30:07.000+0000", "updated": "2016-11-03T13:38:45.000+0000" }, { "id": "400359", "author": { "name": "CollinPrice", "key": "collinprice", "displayName": "Collin Price", "active": true, "timeZone": "America/Toronto" }, "body": "Is there documentation for the accessControlMode and accessibilityMode iOS arguments?", "updateAuthor": { "name": "CollinPrice", "key": "collinprice", "displayName": "Collin Price", "active": true, "timeZone": "America/Toronto" }, "created": "2016-11-03T19:26:23.000+0000", "updated": "2016-11-03T19:26:23.000+0000" }, { "id": "400364", "author": { "name": "nradaev", "key": "nradaev", "displayName": "Nikita Radaev", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Could you please provide reference for accessControlMode possible values?", "updateAuthor": { "name": "nradaev", "key": "nradaev", "displayName": "Nikita Radaev", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2016-11-03T20:42:12.000+0000", "updated": "2016-11-03T20:42:12.000+0000" }, { "id": "400402", "author": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "body": "Chillll :-) It's [here|https://github.com/hansemannn/ti.touchid/blob/fa99e6cdf935acabd51f06baa61a939b3966485a/apidoc/TouchId.yml#L541-L556] with some constants to use there like advised natively.", "updateAuthor": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "created": "2016-11-04T14:13:22.000+0000", "updated": "2016-11-04T14:13:22.000+0000" }, { "id": "400420", "author": { "name": "nradaev", "key": "nradaev", "displayName": "Nikita Radaev", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Hi Hans,\r\n\r\nI've done a bit of testing using your examples above and here are my results: \r\n\r\nCase 1.\r\n\r\n1) There is an error in the docs stating that for iOS 10 and up I need to add entitlements file in order to access keychain. I had to do that on my test device that is running iOS 8.2, otherwise callback was successfull, but I could see native iOS error being thrown. I was also not able to retrieve saved keychain item in that case.\r\n2) Saving to keychain without using {{accessibilityMode}} and {{accessControlMode}} works as expected\r\n3) Retrieving from keychain works as expected\r\n4) Deleting from keychain works as expected\r\n5) Saving to keychain without using with {{accessControlMode}} flag being set crashes the app without any errors being throw. Tested with all possible values.\r\n6) Saving to keychain using {{accessibilityMode}} does not seem to be working as expected for values ending with {{*THIS_DEVICE_ONLY}}. I've saved to keychain with this flag, backed up device, restored backup on another device and I was still able to retrieve item from keychain.\r\n\r\nCase 2. \r\n\r\n1) {{TiTouchId.isSupported}} does not work as intended. In case neither password/fingerprint are set up, {{TiTouchId.isSupported}}\r\nreturns false on devices that actually support the feature (Tested on devices with/without the fingerprint reader). \r\n\r\n2) Using {{TouchID.authenticate}} without checking {{TiTouchId.isSupported}}: \r\n* Passcode turned off, no fingerprints added -> recieved {{TiTouchId.ERROR_TOUCH_ID_NOT_AVAILABLE}} instead of {{TiTouchId.ERROR_PASSCODE_NOT_SET}} or {{TiTouchId.ERROR_TOUCH_ID_NOT_ENROLLED}}\r\n* Passcode turned on, no fingerprints added -> recieved {{TiTouchId.ERROR_TOUCH_ID_NOT_AVAILABLE}} instead {{TiTouchId.ERROR_TOUCH_ID_NOT_ENROLLED}}\r\n\r\n3) When touchId is being locked out due to user failing to provide correct fingerprint\r\n* {{maxBiometryFailures}} value does not work, I've set it to values from 1 to 5, I've always been asked 5 times before lockout.\r\n* after being locked out, on iPhone 6 OS v8.2 further attempt to authenticate launch passcode unlock screen. On iPhone7 OS 10.0.2 further attempts to authenticate only yield {{TiTouchId.ERROR_TOUCH_ID_NOT_AVAILABLE}} code being thrown and no unlock modal is presented.\r\n* On iPhone7, after 5th incorrect authentication attempt {{TiTouchId.ERROR_TOUCH_ID_LOCKOUT}} is being thrown, which is correct, but any consecutive tries only cause {{TiTouchId.ERROR_TOUCH_ID_NOT_AVAILABLE}}. I believe either unlock modal should be presented or {{TiTouchId.ERROR_TOUCH_ID_LOCKOUT}} code should be shown.\r\n\r\n4) {{allowableReuseDuration}}, {{fallbackTitle}} and {{cancelTitle}} do not seem to work at all.\r\n\r\n5) All other cases i.e. user cancel, user fallback, authentication failed, authentication passed, system cancel etc work as expected. \r\n\r\n----\r\n\r\nTested with: iPhone6 OS v8.2, iPhone7 OS7 v10.0.2, iPad Mini (without fingerprint scanner), SDK v5.5.1GA, TiTouchId Module v 2.1.0\r\n\r\nSorry for the long post. Please let me know if you have any questions.\r\n", "updateAuthor": { "name": "nradaev", "key": "nradaev", "displayName": "Nikita Radaev", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2016-11-04T17:08:23.000+0000", "updated": "2016-11-04T17:18:30.000+0000" }, { "id": "400429", "author": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "body": "Hey Nikita, addressing your comments line by line:\r\n{quote}\r\n1) There is an error in the docs stating that for iOS 10 and up I need to add entitlements file in order to access keychain. I had to do that on my test device that is running iOS 8.2, otherwise callback was successfull, but I could see native iOS error being thrown. I was also not able to retrieve saved keychain item in that case.\r\n{quote}\r\nSo the keychain access entitlement is also required for iOS 8? Then I'll update the docs. And you meant \"otherwise callback was unsuccessful\", right? Otherwise, I don't get the sentence structure \r\n{quote}\r\n2) Saving to keychain without using accessibilityMode and accessControlMode works as expected\r\n{quote}\r\nOK\r\n {quote}\r\n3) Retrieving from keychain works as expected\r\n{quote}\r\nOK\r\n{quote}\r\n4) Deleting from keychain works as expected\r\n{quote}\r\nOK\r\n{quote}\r\n5) Saving to keychain without using with accessControlMode flag being set crashes the app without any errors being throw. Tested with all possible values.\r\n{quote}\r\nExample please\r\n{quote}\r\n6) Saving to keychain using accessibilityMode does not seem to be working as expected for values ending with *THIS_DEVICE_ONLY. I've saved to keychain with this flag, backed up device, restored backup on another device and I was still able to retrieve item from keychain.\r\nCase 2.\r\n{quote}\r\nExample please\r\n{quote}\r\n1) TiTouchId.isSupported does not work as intended. In case neither password/fingerprint are set up, TiTouchId.isSupported\r\nreturns false on devices that actually support the feature (Tested on devices with/without the fingerprint reader).\r\n2) Using TouchID.authenticate without checking TiTouchId.isSupported:\r\nPasscode turned off, no fingerprints added -> recieved TiTouchId.ERROR_TOUCH_ID_NOT_AVAILABLE instead of TiTouchId.ERROR_PASSCODE_NOT_SET or TiTouchId.ERROR_TOUCH_ID_NOT_ENROLLED\r\nPasscode turned on, no fingerprints added -> recieved TiTouchId.ERROR_TOUCH_ID_NOT_AVAILABLE instead TiTouchId.ERROR_TOUCH_ID_NOT_ENROLLED\r\n3) When touchId is being locked out due to user failing to provide correct fingerprint\r\nmaxBiometryFailures value does not work, I've set it to values from 1 to 5, I've always been asked 5 times before lockout.\r\nafter being locked out, on iPhone 6 OS v8.2 further attempt to authenticate launch passcode unlock screen. On iPhone7 OS 10.0.2 further attempts to authenticate only yield TiTouchId.ERROR_TOUCH_ID_NOT_AVAILABLE code being thrown and no unlock modal is presented.\r\nOn iPhone7, after 5th incorrect authentication attempt TiTouchId.ERROR_TOUCH_ID_LOCKOUT is being thrown, which is correct, but any consecutive tries only cause TiTouchId.ERROR_TOUCH_ID_NOT_AVAILABLE. I believe either unlock modal should be presented or TiTouchId.ERROR_TOUCH_ID_LOCKOUT code should be shown.\r\n{quote}\r\n1) We don't use custom business logic to check this, we just use the system API {{canEvaluatePolicy}}. If your device can use Touch ID but has not been set up to use it, it will return false. And the method didn't change.\r\n2) Same here. We use the {{isSupported}} method internally and will throw {{ERROR_TOUCH_ID_NOT_AVAILABLE}} whenever {{isSupported}} returns {{false}}. But I can see that we can improve this by using {{deviceCanAuthenticate}} instead that also returns the exact error code which we can bubble up. I will add that to the PR. *EDIT*: Changed in the latest commit-sets.\r\n3) I will check {{maxBiometryFailures}} but it did work in my tests-cases before. And what do you mean with \"modal unlock\"? *EDIT*: I removed the property again. It's deprecated since 9.0 (introduced in 8.3) so Apple probably disabled the usage of the property in iOS 10+ already. \r\n{quote}\r\n4) allowableReuseDuration, fallbackTitle and cancelTitle do not seem to work at all.\r\n{quote}\r\nThey all did work for me (see the attached image in the Github release page). Ensure to call it as the params of the {{authenticate}} method. *EDIT*: After some checks, I found out that this property is only used for authentications from the lock-screen, not in-app: \"This property is meant only for reusing Touch ID matches from the device lock screen. \"It does not allow reusing previous Touch ID matches in application or between applications.\"\r\n{quote}\r\n5) All other cases i.e. user cancel, user fallback, authentication failed, authentication passed, system cancel etc work as expected.\r\n{quote}\r\nOK\r\n\r\nAnd thank you very much for the detailed review, appreciate that!\r\n\r\n*EDIT*: 2.1.0 Beta 2 is out, having all fixed listed in https://github.com/appcelerator-modules/ti.touchid/releases/tag/2.1.0-beta.2", "updateAuthor": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "created": "2016-11-04T20:59:37.000+0000", "updated": "2016-11-05T18:15:45.000+0000" }, { "id": "400464", "author": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "body": "Oh, and the more I think about this, the more I would like to remove {{writeToKeychain}}, {{readFromKeychain}} and {{deleteFromKeychain}} and use an own namespace for managing them. Like this:\r\n{code:javascript}\r\nva keychainItem = TouchID.createKeychainItem({\r\n identifier: \"password\",\r\n accessGroup: \".com.appc.touchidtest\",\r\n // More configuration like accessibilityMode and accessControlMode\r\n});\r\n\r\nkeychainItem.addEventListener(\"read\", function(e) {\r\n // A keychain item was read or failed\r\n});\r\n\r\nkeychainItem.addEventListener(\"write\", function(e) {\r\n // A keychain item was written or failed\r\n});\r\n\r\nkeychainItem.addEventListener(\"delete\", function(e) {\r\n // A keychain item was deleted or failed\r\n});\r\n\r\nkeychainItem.write(\"my_value\"); // Will trigger \"write\" event\r\nkeychainItem.read(); // Will trigger \"read\" event\r\nkeychainItem.delete(); // Will trigger \"delete\" event\r\n{code}\r\n\r\nAnd maybe some additional utilities:\r\n{code:javascript}\r\nvar exists = keychainItem.exists();\r\nvar isValid = keychainItem.isValid();\r\n{code}\r\n\r\nSo we either put this interface changes in this PR or merge it and do an additional ticket where we deprecate the old one again. ", "updateAuthor": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "created": "2016-11-06T14:40:35.000+0000", "updated": "2016-11-06T14:43:18.000+0000" }, { "id": "400520", "author": { "name": "nradaev", "key": "nradaev", "displayName": "Nikita Radaev", "active": true, "timeZone": "America/Los_Angeles" }, "body": "HI Hans,\r\n\r\nThank you for timely and descriptive response. Here are my test notes for updated module: \r\n\r\nCase 1.\r\n\r\n- {{accessControlMode}} no longer crashes the app when set. However, I've tried different values and they do not seem to have any effect. I'll post my exact test code below.\r\n- {{accessibilityMode}} also appears to do nothing. I've tried all possible values and they do not seem to alter app behaviour.\r\n\r\nBased off that, I suspect I might not be using those flags correctly. Please refer to test code below.\r\n\r\nCase 2.\r\n\r\n- I was able to confirm that {{ERROR_TOUCH_ID_NOT_ENROLLED}} and {{ERROR_PASSCODE_NOT_SET}} messages are now working correctly.\r\n- When user chooses to enter passcode after unsuccessful fingerprint authentication, correct message ({{ERROR_USER_FALLBACK}}) is being thrown. However, no dialogue to enter passcode is presented. This worked correctly in the previous iteration of the module.\r\n\r\nWhen user is being locked out due to maximum unsuccessful authentication attempts\r\n* On iPhone 6 OS v8.2 incorrect message is being thrown {{ERROR_AUTHENTICATION_FAILED.}} Subsequent authentication attempt launches passcode modal. When user enters correct passcode he is able to try to authenticate with fingerprint again\r\n* On iPhone 7 OS v10.0.2 correct message is being thrown {{ERROR_TOUCH_ID_LOCKOUT}}. Subsequent authentication attempt throws same message again. Enter passcode modal dialogue is not being presented.\r\n\r\nI believe both cases should yield same result: {{ERROR_TOUCH_ID_LOCKOUT}} message when lockout happens, passcode modal dialogue on subsequent attempts to authenticate.\r\n\r\nLastly, I appreciate your comment regarding {{isSupported}} functionality. Its weird that it works that way, especially given the fact that {{TouchID.authenticate}} is actually able to make a distinction between not having fingerprint scanner, password not being set and not having any fingerprints in the system. Is there any way you could leverage {{TouchID.authenticate}} functionality to enhance {{isSupported}}. \r\n\r\n----\r\n\r\nMy code:\r\n\r\nindex.xml:\r\n\r\n{code:java}\r\n\r\n \r\n