├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── Cartfile ├── Cartfile.private ├── Cartfile.resolved ├── Flow.png ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── MyTouchID ├── A0RegisterViewController.h ├── A0RegisterViewController.m ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── logo-horizontal-blue.imageset │ │ ├── Contents.json │ │ ├── logo-horizontal-blue.png │ │ ├── logo-horizontal-blue@2x.png │ │ └── logo-horizontal-blue@3x.png │ └── touchid.imageset │ │ ├── Contents.json │ │ ├── touchid.png │ │ ├── touchid@2x.png │ │ └── touchid@3x.png ├── Base.lproj │ └── LaunchScreen.storyboard ├── Info.plist ├── Main.storyboard ├── ViewController.h ├── ViewController.m └── main.m ├── README.md ├── TouchIDAuth.podspec ├── TouchIDAuth.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ └── TouchIDAuth-iOS.xcscheme ├── TouchIDAuth ├── A0JWTBuilder.h ├── A0JWTBuilder.m ├── A0RSAKeyExporter.h ├── A0RSAKeyExporter.m ├── A0TouchID.h ├── A0TouchID.m ├── A0TouchIDAuthentication.h ├── A0TouchIDAuthentication.m ├── Info.plist ├── NSData+A0JWTSafeBase64.h ├── NSData+A0JWTSafeBase64.m └── TouchIDAuth.h ├── TouchIDAuthServer ├── app.js └── package.json ├── TouchIDAuthTests ├── A0JWTSafeBase64Spec.swift ├── A0RSAKeyExporterSpec.swift ├── A0TouchIDAuthenticationSpec.swift ├── Info.plist └── TouchIDAuthTests-Bridging-Header.h └── script ├── .env ├── bootstrap ├── build ├── certificates └── cibot.p12 ├── cibuild ├── common └── carthage ├── coverage ├── git_hooks └── pre-push ├── script_hooks ├── .gitkeep ├── destinations └── schemes ├── test └── update /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | .bundle 24 | 25 | # CocoaPods 26 | Pods/ 27 | 28 | #Carthage 29 | Carthage/ 30 | 31 | #NodeJS 32 | node_modules 33 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | reference: http://www.objc.io/issue-6/travis-ci.html 2 | language: objective-c 3 | osx_image: xcode7.1 4 | before_install: true 5 | install: true 6 | script: 7 | - script/cibuild 8 | branches: 9 | only: 10 | - master 11 | notifications: 12 | slack: 13 | secure: FYTAD2dSWOSaIV9f2c+3RmX5ib+aJuTioNAq+RGK3S9dbmBgRDuPainIbtZX5uDBWkTHDaVe6ydELB+HLkt8zccGjU9Rc8n1HQqn86QjGQXFxnCILMrUQu0utzHgXGy6XDCVkO6pCGHPw0GCes2KUZ7JyohJz2tx1aOLxI0DC4E= 14 | env: 15 | global: 16 | - secure: Lije1gxftTmnm/2XVjA5nYKbfhkH/kgl4T0VfxEtDeajn/grvMJvmk6NEgKI/sEqu9C7GndY9rlxWGZ3u9U2eKJkUt/9m9BLBMcGDzaqoSAw8gZHiJVr6TiVFTKrqjCEKsobwxjhQgVo8J664fbRDRN65RwNoAjFqSyv2zTtxDo= 17 | - secure: B4hN2R9S/9dynevb+z8PE8KHebjcq8cwAnOwQQxfYZhgzY2s6aUbw8bPP5myE0qgU9k1BVLiA/MbVs5WDs0yjv957m2093OEUCFFk7H8Z+hN8+JlnFtcBQR2yxRpQ+ICWjfzhGtf2EyrZV56w67HOMQO11OPeuH1CV7iXS2dxVs= 18 | matrix: 19 | - SCHEME="TouchIDAuth-iOS" IOS_DESTINATION_VERSION="9.1" CARTHAGE_BUILD_PLATFORM="iOS" 20 | #- SCHEME="TouchIDAuth-iOS" IOS_DESTINATION_VERSION="9.0" CARTHAGE_BUILD_PLATFORM="iOS" 21 | #- SCHEME="TouchIDAuth-iOS" IOS_DESTINATION_VERSION="8.4" CARTHAGE_BUILD_PLATFORM="iOS" 22 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [0.2.0](https://github.com/auth0/TouchIDAuth/tree/0.2.0) (2015-10-30) 4 | [Full Changelog](https://github.com/auth0/TouchIDAuth/compare/0.1.2...0.2.0) 5 | 6 | **Merged pull requests:** 7 | 8 | - Carthage Support [\#4](https://github.com/auth0/TouchIDAuth/pull/4) ([hzalaz](https://github.com/hzalaz)) 9 | - Clean up dependencies and project [\#3](https://github.com/auth0/TouchIDAuth/pull/3) ([hzalaz](https://github.com/hzalaz)) 10 | 11 | ## [0.1.2](https://github.com/auth0/TouchIDAuth/tree/0.1.2) (2015-10-08) 12 | [Full Changelog](https://github.com/auth0/TouchIDAuth/compare/0.1.1...0.1.2) 13 | 14 | **Merged pull requests:** 15 | 16 | - Remove extra release of key [\#2](https://github.com/auth0/TouchIDAuth/pull/2) ([hzalaz](https://github.com/hzalaz)) 17 | 18 | ## [0.1.1](https://github.com/auth0/TouchIDAuth/tree/0.1.1) (2015-03-29) 19 | [Full Changelog](https://github.com/auth0/TouchIDAuth/compare/0.1.0...0.1.1) 20 | 21 | ## [0.1.0](https://github.com/auth0/TouchIDAuth/tree/0.1.0) (2014-11-07) 22 | 23 | 24 | \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "auth0/SimpleKeychain" ~> 0.6 -------------------------------------------------------------------------------- /Cartfile.private: -------------------------------------------------------------------------------- 1 | github "jdg/MBProgressHUD" "master" 2 | github "AFNetworking/AFNetworking" "3.0.0-beta.1" 3 | github "auth0/JWTDecode.swift" ~> 1.0 4 | github "Quick/Nimble" "v2.0.0" 5 | github "Quick/Quick" "v0.6.0" -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "AFNetworking/AFNetworking" "3.0.0-beta.1" 2 | github "auth0/JWTDecode.swift" "1.0.0" 3 | github "jdg/MBProgressHUD" "f089d2a035df0044714d6a80f614ba35f29c9baa" 4 | github "Quick/Nimble" "v2.0.0" 5 | github "Quick/Quick" "v0.6.0" 6 | github "auth0/SimpleKeychain" "0.6.1" 7 | -------------------------------------------------------------------------------- /Flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auth0/TouchIDAuth/78f5130a66e58d7ee533ed9f75fe991f84460ad3/Flow.png -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'cocoapods', '~> 0.39' 4 | gem 'xcpretty' -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | activesupport (4.2.4) 5 | i18n (~> 0.7) 6 | json (~> 1.7, >= 1.7.7) 7 | minitest (~> 5.1) 8 | thread_safe (~> 0.3, >= 0.3.4) 9 | tzinfo (~> 1.1) 10 | claide (0.9.1) 11 | cocoapods (0.39.0) 12 | activesupport (>= 4.0.2) 13 | claide (~> 0.9.1) 14 | cocoapods-core (= 0.39.0) 15 | cocoapods-downloader (~> 0.9.3) 16 | cocoapods-plugins (~> 0.4.2) 17 | cocoapods-search (~> 0.1.0) 18 | cocoapods-stats (~> 0.6.2) 19 | cocoapods-trunk (~> 0.6.4) 20 | cocoapods-try (~> 0.5.1) 21 | colored (~> 1.2) 22 | escape (~> 0.0.4) 23 | molinillo (~> 0.4.0) 24 | nap (~> 1.0) 25 | xcodeproj (~> 0.28.2) 26 | cocoapods-core (0.39.0) 27 | activesupport (>= 4.0.2) 28 | fuzzy_match (~> 2.0.4) 29 | nap (~> 1.0) 30 | cocoapods-downloader (0.9.3) 31 | cocoapods-plugins (0.4.2) 32 | nap 33 | cocoapods-search (0.1.0) 34 | cocoapods-stats (0.6.2) 35 | cocoapods-trunk (0.6.4) 36 | nap (>= 0.8, < 2.0) 37 | netrc (= 0.7.8) 38 | cocoapods-try (0.5.1) 39 | colored (1.2) 40 | escape (0.0.4) 41 | fuzzy_match (2.0.4) 42 | i18n (0.7.0) 43 | json (1.8.3) 44 | minitest (5.8.2) 45 | molinillo (0.4.0) 46 | nap (1.0.0) 47 | netrc (0.7.8) 48 | rouge (1.10.1) 49 | thread_safe (0.3.5) 50 | tzinfo (1.2.2) 51 | thread_safe (~> 0.1) 52 | xcodeproj (0.28.2) 53 | activesupport (>= 3) 54 | claide (~> 0.9.1) 55 | colored (~> 1.2) 56 | xcpretty (0.2.1) 57 | rouge (~> 1.8) 58 | 59 | PLATFORMS 60 | ruby 61 | 62 | DEPENDENCIES 63 | cocoapods (~> 0.39) 64 | xcpretty 65 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Auth0 LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MyTouchID/A0RegisterViewController.h: -------------------------------------------------------------------------------- 1 | // A0RegisterViewController.h 2 | // 3 | // Copyright (c) 2015 Auth0 (http://auth0.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import 24 | 25 | @interface A0RegisterViewController : UIViewController 26 | 27 | @property (weak, nonatomic) IBOutlet UITextField *emailField; 28 | 29 | - (IBAction)registerEmail:(id)sender; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /MyTouchID/A0RegisterViewController.m: -------------------------------------------------------------------------------- 1 | // A0RegisterViewController.m 2 | // 3 | // Copyright (c) 2015 Auth0 (http://auth0.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import "A0RegisterViewController.h" 24 | 25 | @interface A0RegisterViewController () 26 | 27 | @end 28 | 29 | @implementation A0RegisterViewController 30 | 31 | - (void)viewDidLoad { 32 | [super viewDidLoad]; 33 | self.navigationItem.hidesBackButton = YES; 34 | [self.emailField becomeFirstResponder]; 35 | } 36 | 37 | - (void)registerEmail:(id)sender { 38 | if (self.emailField.text.length > 0) { 39 | [self performSegueWithIdentifier:@"RegisterEmailUser" sender:self]; 40 | } 41 | } 42 | @end 43 | -------------------------------------------------------------------------------- /MyTouchID/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // AppDelegate.h 2 | // 3 | // Copyright (c) 2015 Auth0 (http://auth0.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import 24 | 25 | @interface AppDelegate : UIResponder 26 | 27 | @property (strong, nonatomic) UIWindow *window; 28 | 29 | 30 | @end 31 | 32 | -------------------------------------------------------------------------------- /MyTouchID/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // AppDelegate.m 2 | // 3 | // Copyright (c) 2015 Auth0 (http://auth0.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import "AppDelegate.h" 24 | 25 | @interface AppDelegate () 26 | 27 | @end 28 | 29 | @implementation AppDelegate 30 | 31 | 32 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 33 | // Override point for customization after application launch. 34 | return YES; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /MyTouchID/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /MyTouchID/Assets.xcassets/logo-horizontal-blue.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "logo-horizontal-blue.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "logo-horizontal-blue@2x.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x", 16 | "filename" : "logo-horizontal-blue@3x.png" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /MyTouchID/Assets.xcassets/logo-horizontal-blue.imageset/logo-horizontal-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auth0/TouchIDAuth/78f5130a66e58d7ee533ed9f75fe991f84460ad3/MyTouchID/Assets.xcassets/logo-horizontal-blue.imageset/logo-horizontal-blue.png -------------------------------------------------------------------------------- /MyTouchID/Assets.xcassets/logo-horizontal-blue.imageset/logo-horizontal-blue@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auth0/TouchIDAuth/78f5130a66e58d7ee533ed9f75fe991f84460ad3/MyTouchID/Assets.xcassets/logo-horizontal-blue.imageset/logo-horizontal-blue@2x.png -------------------------------------------------------------------------------- /MyTouchID/Assets.xcassets/logo-horizontal-blue.imageset/logo-horizontal-blue@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auth0/TouchIDAuth/78f5130a66e58d7ee533ed9f75fe991f84460ad3/MyTouchID/Assets.xcassets/logo-horizontal-blue.imageset/logo-horizontal-blue@3x.png -------------------------------------------------------------------------------- /MyTouchID/Assets.xcassets/touchid.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "touchid.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "touchid@2x.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x", 16 | "filename" : "touchid@3x.png" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /MyTouchID/Assets.xcassets/touchid.imageset/touchid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auth0/TouchIDAuth/78f5130a66e58d7ee533ed9f75fe991f84460ad3/MyTouchID/Assets.xcassets/touchid.imageset/touchid.png -------------------------------------------------------------------------------- /MyTouchID/Assets.xcassets/touchid.imageset/touchid@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auth0/TouchIDAuth/78f5130a66e58d7ee533ed9f75fe991f84460ad3/MyTouchID/Assets.xcassets/touchid.imageset/touchid@2x.png -------------------------------------------------------------------------------- /MyTouchID/Assets.xcassets/touchid.imageset/touchid@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auth0/TouchIDAuth/78f5130a66e58d7ee533ed9f75fe991f84460ad3/MyTouchID/Assets.xcassets/touchid.imageset/touchid@3x.png -------------------------------------------------------------------------------- /MyTouchID/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /MyTouchID/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 0.2.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /MyTouchID/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | -------------------------------------------------------------------------------- /MyTouchID/ViewController.h: -------------------------------------------------------------------------------- 1 | // ViewController.h 2 | // 3 | // Copyright (c) 2015 Auth0 (http://auth0.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import 24 | 25 | @interface ViewController : UIViewController 26 | 27 | 28 | @end 29 | 30 | -------------------------------------------------------------------------------- /MyTouchID/ViewController.m: -------------------------------------------------------------------------------- 1 | // ViewController.m 2 | // 3 | // Copyright (c) 2015 Auth0 (http://auth0.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import "ViewController.h" 24 | 25 | #import 26 | #import 27 | #import 28 | 29 | #import "A0RegisterViewController.h" 30 | 31 | #define kBaseURL @"http://localhost:3000" 32 | 33 | @interface ViewController () 34 | @property (strong, nonatomic) A0TouchIDAuthentication *authentication; 35 | 36 | @property (copy, nonatomic) void(^completionBlock)(NSString *email); 37 | 38 | - (IBAction)startAuthentication:(id)sender; 39 | - (IBAction)userRegistered:(UIStoryboardSegue *)segue; 40 | @end 41 | 42 | @implementation ViewController 43 | 44 | - (void)viewDidLoad { 45 | [super viewDidLoad]; 46 | self.authentication = [[A0TouchIDAuthentication alloc] init]; 47 | [self.authentication reset]; 48 | NSURL *baseURL = [NSURL URLWithString:kBaseURL]; 49 | AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithBaseURL:baseURL]; 50 | manager.requestSerializer = [AFJSONRequestSerializer serializer]; 51 | manager.responseSerializer = [AFJSONResponseSerializer serializer]; 52 | [self.authentication reset]; 53 | __weak ViewController *weakSelf = self; 54 | self.authentication.registerPublicKey = ^(NSData *pubKey, A0RegisterCompletionBlock completionBlock, A0ErrorBlock errorBlock) { 55 | weakSelf.completionBlock = ^(NSString *email) { 56 | MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:weakSelf.view animated:YES]; 57 | hud.labelText = NSLocalizedString(@"Registering Public Key...", nil); 58 | NSDictionary *params = @{ 59 | @"user": email, 60 | @"key": [pubKey base64EncodedStringWithOptions:0], 61 | }; 62 | [manager POST:@"/pubkey" parameters:params success:^(NSURLSessionTask *task, id responseObject) { 63 | completionBlock(); 64 | } failure:^(NSURLSessionTask *task, NSError *error) { 65 | errorBlock(error); 66 | [MBProgressHUD hideAllHUDsForView:weakSelf.view animated:YES]; 67 | }]; 68 | }; 69 | [weakSelf performSegueWithIdentifier:@"StartRegister" sender:weakSelf]; 70 | }; 71 | 72 | self.authentication.authenticate = ^(NSString *jwt, A0ErrorBlock errorBlock) { 73 | NSLog(@"JWT: %@", jwt); 74 | NSDictionary *params = @{ 75 | @"jwt": jwt, 76 | }; 77 | MBProgressHUD *hud = [MBProgressHUD HUDForView:weakSelf.view]; 78 | hud.labelText = NSLocalizedString(@"Login in with JWT...", nil); 79 | [manager POST:@"/login" parameters:params success:^(NSURLSessionTask *task, id responseObject) { 80 | NSLog(@"Logged in!!!"); 81 | [MBProgressHUD hideAllHUDsForView:weakSelf.view animated:YES]; 82 | [weakSelf performSegueWithIdentifier:@"Authenticated" sender:weakSelf]; 83 | } failure:^(NSURLSessionTask *task, NSError *error) { 84 | errorBlock(error); 85 | [MBProgressHUD hideAllHUDsForView:weakSelf.view animated:YES]; 86 | }]; 87 | }; 88 | self.authentication.onError = ^(NSError *error) { 89 | NSLog(@"ERROR %@", error); 90 | [MBProgressHUD hideAllHUDsForView:weakSelf.view animated:YES]; 91 | }; 92 | } 93 | 94 | - (void)startAuthentication:(id)sender { 95 | [self.authentication start]; 96 | } 97 | 98 | - (void)userRegistered:(UIStoryboardSegue *)segue { 99 | A0RegisterViewController *controller = segue.sourceViewController; 100 | if (self.completionBlock) { 101 | self.completionBlock(controller.emailField.text); 102 | self.completionBlock = nil; 103 | } 104 | } 105 | @end 106 | -------------------------------------------------------------------------------- /MyTouchID/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // MyTouchID 4 | // 5 | // Created by Hernan Zalazar on 10/29/15. 6 | // Copyright © 2015 Auth0. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TouchIDAuth 2 | 3 | [![CI Status](http://img.shields.io/travis/auth0/TouchIDAuth.svg?style=flat)](https://travis-ci.org/auth0/TouchIDAuth) 4 | [![Version](https://img.shields.io/cocoapods/v/TouchIDAuth.svg?style=flat)](http://cocoadocs.org/docsets/TouchIDAuth) 5 | [![License](https://img.shields.io/cocoapods/l/TouchIDAuth.svg?style=flat)](http://cocoadocs.org/docsets/TouchIDAuth) 6 | [![Platform](https://img.shields.io/cocoapods/p/TouchIDAuth.svg?style=flat)](http://cocoadocs.org/docsets/TouchIDAuth) 7 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 8 | 9 | TouchIDAuth provides a default implementation for a passwordless login flow using TouchID and JWT. 10 | 11 | ![TouchID Flow](/Flow.png "TouchID Flow") 12 | 13 | The flow is represented in the following graph: 14 | 15 | 1. Validate the user presence using TouchID 16 | 1. Check if the user has a key pair 17 | 1. Generate a key pair 18 | 1. Register the Public Key for the user. 19 | 1. Generate a JWT and signs it with the Private Key using **RS256** 20 | 1. Authenticate the user with the signed JWT. 21 | 22 | ![TouchID demo](https://cloudup.com/cyDR07xBX3j+) 23 | 24 | ## Requirements 25 | 26 | The library requires iOS 8+ and a device with TouchID. 27 | 28 | ## Installation 29 | 30 | ### CocoaPods 31 | 32 | TouchIDAuth is available through [CocoaPods](http://cocoapods.org). To install 33 | it, simply add the following line to your Podfile: 34 | 35 | pod "TouchIDAuth" 36 | 37 | ### Carthage 38 | 39 | In your Cartfile add the following 40 | 41 | ``` 42 | github "auth0/TouchIDAuth" 43 | ``` 44 | 45 | 46 | ## Running Example app 47 | 48 | To run the example project, clone the repo, and run `pod install` from the Example directory first. 49 | And then in [A0ViewController](https://github.com/auth0/TouchIDAuth/blob/master/MyTouchID/ViewController.m#L32) add your IP address or hostname for the mock server URL: 50 | 51 | ```objc 52 | #define kBaseURL @"http://mymac.local:3000" 53 | ``` 54 | 55 | To run the mock server, go to the folder [TouchIDAuthServer](https://github.com/auth0/TouchIDAuth/tree/master/TouchIDAuthServer) and run the following commands: 56 | ```bash 57 | npm install 58 | node app.js 59 | ``` 60 | 61 | ## Usage 62 | 63 | First you'll need to instantiate it 64 | ```objc 65 | A0TouchIDAuthentication *authentication = [[A0TouchIDAuthentication alloc] init]; 66 | ``` 67 | Then you need to configure a couple of callbacks that will be called during the authentication flow. There are three callbacks, `registerPublicKey`, `jwtPayload` and `authenticate`. 68 | 69 | The callback `registerPublicKey` will handle the registration of the public key against an API and must call `completionBlock` on success in order to continue with the flow (or `errorBlock` if it fails). For example: 70 | 71 | ```objc 72 | authentication.registerPublicKey = ^(NSData *pubKey, A0RegisterCompletionBlock completionBlock, A0ErrorBlock errorBlock) { 73 | AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:baseURL]; 74 | //Configure AFHTTPRequestOperationManager 75 | [manager POST:@"/pubkey" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) { 76 | completionBlock(); 77 | } failure:^(AFHTTPRequestOperation *operation, NSError *error) { 78 | errorBlock(error); 79 | }]; 80 | }; 81 | }; 82 | ``` 83 | 84 | The callback `jwtPayload` is called before generating the JWT in order to provide the JWT payload needed by your API endpoint. For example: 85 | 86 | ```objc 87 | authentication.jwtPayload = ^{ 88 | return @{ 89 | @"iss": @"Issuer", 90 | @"custom_key": @"value", 91 | }; 92 | }; 93 | ``` 94 | The callback `authenticate` will receive the signed JWT and will need to authenticate against your API endpoint. For example: 95 | 96 | ```objc 97 | authentication.authenticate = ^(NSString *jwt, A0ErrorBlock errorBlock) { 98 | NSDictionary *params = @{ 99 | @"jwt": jwt, 100 | }; 101 | AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:baseURL]; 102 | //Configure AFHTTPRequestOperationManager 103 | [manager POST:@"/login" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) { 104 | NSLog(@"Logged in!!!"); 105 | } failure:^(AFHTTPRequestOperation *operation, NSError *error) { 106 | errorBlock(error); 107 | }]; 108 | }; 109 | ``` 110 | 111 | There is an extra callback `onError` that will be called whenever an error ocurrs while executing the Auth flow: 112 | ```objc 113 | authentication.onError = ^(NSError *error) { 114 | NSLog(@"ERROR %@", error); 115 | }; 116 | ``` 117 | 118 | At last, call the following method to start the authentication flow: 119 | ```objc 120 | [authentication start]; 121 | ``` 122 | > Before calling `start`, it's recommended to check if **TouchID** is enabled in the device calling the method [isTouchIDAuthenticationAvailable](#a0touchidauthenticationistouchidauthenticationavailable). 123 | 124 | ##API 125 | 126 | ###A0TouchIDAuthentication 127 | 128 | ####A0TouchIDAuthentication#registerPublicKey 129 | ```objc 130 | @property (copy, nonatomic) void(^registerPublicKey)(NSData *pubKey, A0RegisterCompletionBlock completionBlock, A0ErrorBlock errorBlock); 131 | ``` 132 | Block to handle public key registration with an API Endpoint. It will receive 3 parameters: publicKey, completionBlock and errorBlock. The public key is formatted as a RSA public key. 133 | 134 | ####A0TouchIDAuthentication#jwtPayload 135 | ```objc 136 | @property (copy, nonatomic) NSDictionary *(^jwtPayload)(); 137 | ``` 138 | Block to return the paylod for the JWT to be signed by the device. It will be called each time a JWT needs to be generated and signed. By default `A0TouchIDAuth` will include `iat`, `exp` (30 sec) and `sub` (Public Key fingerprint) claims but you can override them or add more entries to the payload. 139 | 140 | ####A0TouchIDAuthentication#authenticate 141 | ```objc 142 | @property (copy, nonatomic) void(^authenticate)(NSString *jwt, A0ErrorBlock errorBlock); 143 | ``` 144 | Block called with the signed JWT to authenticate against an API ednpoint. 145 | 146 | ####A0TouchIDAuthentication#onError 147 | ```objc 148 | @property (copy, nonatomic) void(^onError)(NSError *error); 149 | ``` 150 | Block called when an error occurred during the Authentication flow. 151 | 152 | ####A0TouchIDAuthentication#localizedTouchIDMessage 153 | ```objc 154 | @property (copy, nonatomic) NSString *localizedTouchIDMessage; 155 | ``` 156 | Localized message displayed in TouchID prompt. 157 | 158 | ####A0TouchIDAuthentication#start 159 | ```objc 160 | - (void)start; 161 | ``` 162 | Starts the TouchID authentication flow. It will fail automatically if `isTouchIDAuthenticationAvailable` returns `NO`. 163 | 164 | ####A0TouchIDAuthentication#isTouchIDAuthenticationAvailable 165 | ```objc 166 | - (BOOL)isTouchIDAuthenticationAvailable; 167 | ``` 168 | Check if TouchID is supported by the device and configured. 169 | 170 | ####A0TouchIDAuthentication#reset 171 | ```objc 172 | - (void)reset; 173 | ``` 174 | Reset TouchID authentication info stored in the device. 175 | 176 | ## Issue Reporting 177 | 178 | If you have found a bug or if you have a feature request, please report them at this repository issues section. Please do not report security vulnerabilities on the public GitHub issue tracker. The [Responsible Disclosure Program](https://auth0.com/whitehat) details the procedure for disclosing security issues. 179 | 180 | ## Author 181 | 182 | [Auth0](auth0.com) 183 | 184 | ## What is Auth0? 185 | 186 | Auth0 helps you to: 187 | 188 | * Add authentication with [multiple authentication sources](https://docs.auth0.com/identityproviders), either social like **Google, Facebook, Microsoft Account, LinkedIn, GitHub, Twitter, Box, Salesforce, amont others**, or enterprise identity systems like **Windows Azure AD, Google Apps, Active Directory, ADFS or any SAML Identity Provider**. 189 | * Add authentication through more traditional **[username/password databases](https://docs.auth0.com/mysql-connection-tutorial)**. 190 | * Add support for **[linking different user accounts](https://docs.auth0.com/link-accounts)** with the same user. 191 | * Support for generating signed [Json Web Tokens](https://docs.auth0.com/jwt) to call your APIs and **flow the user identity** securely. 192 | * Analytics of how, when and where users are logging in. 193 | * Pull data from other sources and add it to the user profile, through [JavaScript rules](https://docs.auth0.com/rules). 194 | 195 | ## Create a free account in Auth0 196 | 197 | 1. Go to [Auth0](https://auth0.com) and click Sign Up. 198 | 2. Use Google, GitHub or Microsoft Account to login. 199 | 200 | ## License 201 | 202 | TouchIDAuth is available under the MIT license. See the [LICENSE file](https://github.com/auth0/TouchIDAuth/blob/master/LICENSE) for more info. 203 | -------------------------------------------------------------------------------- /TouchIDAuth.podspec: -------------------------------------------------------------------------------- 1 | version = `agvtool mvers -terse1`.strip 2 | Pod::Spec.new do |s| 3 | s.name = "TouchIDAuth" 4 | s.version = version 5 | s.summary = "A library for passwordless authentication using TouchID & JWT" 6 | s.description = <<-DESC 7 | iOS library that implements a passwordless flow using TouchID & JWT. 8 | The authentication flow has these steps: 9 | 10 | * TouchID validation 11 | * Public/Private Key handling 12 | * JWT generation & signing 13 | 14 | It provides callbacks to implement the interaction with your backend in order to: 15 | 16 | * Associate a public key to a user (Used to validate the signed JWT) 17 | * Authenticate using the generated JWT. 18 | DESC 19 | s.homepage = "https://github.com/auth0/TouchIDAuth" 20 | s.license = 'MIT' 21 | s.author = { "Hernan Zalazar" => "hernan@auth0.com" } 22 | s.source = { :git => "https://github.com/auth0/TouchIDAuth.git", :tag => s.version.to_s } 23 | s.social_media_url = 'https://twitter.com/auth0' 24 | 25 | s.platform = :ios, '7.0' 26 | s.requires_arc = true 27 | 28 | s.source_files = 'TouchIDAuth/*.{h,m}' 29 | 30 | s.frameworks = 'Security', 'LocalAuthentication' 31 | s.dependency 'SimpleKeychain', '~> 0.3' 32 | end 33 | -------------------------------------------------------------------------------- /TouchIDAuth.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 5F4EBC8F1BE2AAEC00252D33 /* TouchIDAuth.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F4EBC8E1BE2AAEC00252D33 /* TouchIDAuth.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | 5F4EBCA01BE2AC5E00252D33 /* A0JWTBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F4EBC961BE2AC5E00252D33 /* A0JWTBuilder.h */; }; 12 | 5F4EBCA11BE2AC5E00252D33 /* A0JWTBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F4EBC971BE2AC5E00252D33 /* A0JWTBuilder.m */; }; 13 | 5F4EBCA21BE2AC5E00252D33 /* A0RSAKeyExporter.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F4EBC981BE2AC5E00252D33 /* A0RSAKeyExporter.h */; }; 14 | 5F4EBCA31BE2AC5E00252D33 /* A0RSAKeyExporter.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F4EBC991BE2AC5E00252D33 /* A0RSAKeyExporter.m */; }; 15 | 5F4EBCA41BE2AC5E00252D33 /* A0TouchID.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F4EBC9A1BE2AC5E00252D33 /* A0TouchID.h */; }; 16 | 5F4EBCA51BE2AC5E00252D33 /* A0TouchID.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F4EBC9B1BE2AC5E00252D33 /* A0TouchID.m */; }; 17 | 5F4EBCA61BE2AC5E00252D33 /* A0TouchIDAuthentication.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F4EBC9C1BE2AC5E00252D33 /* A0TouchIDAuthentication.h */; settings = {ATTRIBUTES = (Public, ); }; }; 18 | 5F4EBCA71BE2AC5E00252D33 /* A0TouchIDAuthentication.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F4EBC9D1BE2AC5E00252D33 /* A0TouchIDAuthentication.m */; }; 19 | 5F4EBCA81BE2AC5E00252D33 /* NSData+A0JWTSafeBase64.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F4EBC9E1BE2AC5E00252D33 /* NSData+A0JWTSafeBase64.h */; }; 20 | 5F4EBCA91BE2AC5E00252D33 /* NSData+A0JWTSafeBase64.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F4EBC9F1BE2AC5E00252D33 /* NSData+A0JWTSafeBase64.m */; }; 21 | 5F4EBCAB1BE2AC8C00252D33 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5F4EBCAA1BE2AC8C00252D33 /* Security.framework */; }; 22 | 5F4EBCAD1BE2AC9000252D33 /* LocalAuthentication.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5F4EBCAC1BE2AC9000252D33 /* LocalAuthentication.framework */; }; 23 | 5F4EBCB11BE2C44A00252D33 /* SimpleKeychain.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5F4EBCAF1BE2C44A00252D33 /* SimpleKeychain.framework */; }; 24 | 5F4EBCBB1BE2C5D900252D33 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F4EBCBA1BE2C5D900252D33 /* main.m */; }; 25 | 5F4EBCBE1BE2C5D900252D33 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F4EBCBD1BE2C5D900252D33 /* AppDelegate.m */; }; 26 | 5F4EBCC11BE2C5D900252D33 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F4EBCC01BE2C5D900252D33 /* ViewController.m */; }; 27 | 5F4EBCC61BE2C5D900252D33 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5F4EBCC51BE2C5D900252D33 /* Assets.xcassets */; }; 28 | 5F4EBCC91BE2C5D900252D33 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5F4EBCC71BE2C5D900252D33 /* LaunchScreen.storyboard */; }; 29 | 5F4EBCD21BE2C86F00252D33 /* MBProgressHUD.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5F4EBCD01BE2C86F00252D33 /* MBProgressHUD.framework */; }; 30 | 5F4EBCD71BE2C93E00252D33 /* A0RegisterViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F4EBCD51BE2C93E00252D33 /* A0RegisterViewController.m */; }; 31 | 5F4EBCD81BE2C93E00252D33 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5F4EBCD61BE2C93E00252D33 /* Main.storyboard */; }; 32 | 5F4EBCDB1BE2C9F900252D33 /* AFNetworking.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5F4EBCD91BE2C9F900252D33 /* AFNetworking.framework */; }; 33 | 5F77FA371BE3AB4800DBDE69 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5F77FA321BE3AB0A00DBDE69 /* Nimble.framework */; }; 34 | 5F77FA381BE3AB4800DBDE69 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5F77FA341BE3AB0A00DBDE69 /* Quick.framework */; }; 35 | 5F77FA3B1BE3ADE700DBDE69 /* A0RSAKeyExporterSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F77FA3A1BE3ADE700DBDE69 /* A0RSAKeyExporterSpec.swift */; }; 36 | 5F77FA3E1BE3B22700DBDE69 /* Nimble.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 5F77FA321BE3AB0A00DBDE69 /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 37 | 5F77FA3F1BE3B22700DBDE69 /* Quick.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 5F77FA341BE3AB0A00DBDE69 /* Quick.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 38 | 5F77FA421BE3C30B00DBDE69 /* A0JWTSafeBase64Spec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F77FA411BE3C30B00DBDE69 /* A0JWTSafeBase64Spec.swift */; }; 39 | 5F77FA431BE3C98700DBDE69 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5F4EBCAA1BE2AC8C00252D33 /* Security.framework */; }; 40 | 5F77FA451BE3DC6900DBDE69 /* A0TouchIDAuthenticationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F77FA441BE3DC6900DBDE69 /* A0TouchIDAuthenticationSpec.swift */; }; 41 | 5F77FA471BE3EAC200DBDE69 /* JWTDecode.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5F77FA461BE3EAC200DBDE69 /* JWTDecode.framework */; }; 42 | 5F77FA481BE3EACA00DBDE69 /* JWTDecode.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 5F77FA461BE3EAC200DBDE69 /* JWTDecode.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 43 | /* End PBXBuildFile section */ 44 | 45 | /* Begin PBXContainerItemProxy section */ 46 | 5F4EBCCE1BE2C5E100252D33 /* PBXContainerItemProxy */ = { 47 | isa = PBXContainerItemProxy; 48 | containerPortal = 5F4EBC821BE2AAEC00252D33 /* Project object */; 49 | proxyType = 1; 50 | remoteGlobalIDString = 5F4EBC8A1BE2AAEC00252D33; 51 | remoteInfo = "TouchIDAuth-iOS"; 52 | }; 53 | 5F4EBCEC1BE2CAF900252D33 /* PBXContainerItemProxy */ = { 54 | isa = PBXContainerItemProxy; 55 | containerPortal = 5F4EBC821BE2AAEC00252D33 /* Project object */; 56 | proxyType = 1; 57 | remoteGlobalIDString = 5F4EBCB61BE2C5D900252D33; 58 | remoteInfo = MyTouchID; 59 | }; 60 | /* End PBXContainerItemProxy section */ 61 | 62 | /* Begin PBXCopyFilesBuildPhase section */ 63 | 5F77FA3D1BE3B21C00DBDE69 /* CopyFiles */ = { 64 | isa = PBXCopyFilesBuildPhase; 65 | buildActionMask = 2147483647; 66 | dstPath = ""; 67 | dstSubfolderSpec = 10; 68 | files = ( 69 | 5F77FA481BE3EACA00DBDE69 /* JWTDecode.framework in CopyFiles */, 70 | 5F77FA3E1BE3B22700DBDE69 /* Nimble.framework in CopyFiles */, 71 | 5F77FA3F1BE3B22700DBDE69 /* Quick.framework in CopyFiles */, 72 | ); 73 | runOnlyForDeploymentPostprocessing = 0; 74 | }; 75 | /* End PBXCopyFilesBuildPhase section */ 76 | 77 | /* Begin PBXFileReference section */ 78 | 5F4EBC8B1BE2AAEC00252D33 /* TouchIDAuth.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TouchIDAuth.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 79 | 5F4EBC8E1BE2AAEC00252D33 /* TouchIDAuth.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TouchIDAuth.h; sourceTree = ""; }; 80 | 5F4EBC901BE2AAEC00252D33 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 81 | 5F4EBC961BE2AC5E00252D33 /* A0JWTBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = A0JWTBuilder.h; sourceTree = ""; }; 82 | 5F4EBC971BE2AC5E00252D33 /* A0JWTBuilder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = A0JWTBuilder.m; sourceTree = ""; }; 83 | 5F4EBC981BE2AC5E00252D33 /* A0RSAKeyExporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = A0RSAKeyExporter.h; sourceTree = ""; }; 84 | 5F4EBC991BE2AC5E00252D33 /* A0RSAKeyExporter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = A0RSAKeyExporter.m; sourceTree = ""; }; 85 | 5F4EBC9A1BE2AC5E00252D33 /* A0TouchID.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = A0TouchID.h; sourceTree = ""; }; 86 | 5F4EBC9B1BE2AC5E00252D33 /* A0TouchID.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = A0TouchID.m; sourceTree = ""; }; 87 | 5F4EBC9C1BE2AC5E00252D33 /* A0TouchIDAuthentication.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = A0TouchIDAuthentication.h; sourceTree = ""; }; 88 | 5F4EBC9D1BE2AC5E00252D33 /* A0TouchIDAuthentication.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = A0TouchIDAuthentication.m; sourceTree = ""; }; 89 | 5F4EBC9E1BE2AC5E00252D33 /* NSData+A0JWTSafeBase64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+A0JWTSafeBase64.h"; sourceTree = ""; }; 90 | 5F4EBC9F1BE2AC5E00252D33 /* NSData+A0JWTSafeBase64.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+A0JWTSafeBase64.m"; sourceTree = ""; }; 91 | 5F4EBCAA1BE2AC8C00252D33 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 92 | 5F4EBCAC1BE2AC9000252D33 /* LocalAuthentication.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LocalAuthentication.framework; path = System/Library/Frameworks/LocalAuthentication.framework; sourceTree = SDKROOT; }; 93 | 5F4EBCAF1BE2C44A00252D33 /* SimpleKeychain.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SimpleKeychain.framework; path = Carthage/Build/iOS/SimpleKeychain.framework; sourceTree = ""; }; 94 | 5F4EBCB01BE2C44A00252D33 /* SimpleKeychain.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = SimpleKeychain.framework.dSYM; path = Carthage/Build/iOS/SimpleKeychain.framework.dSYM; sourceTree = ""; }; 95 | 5F4EBCB71BE2C5D900252D33 /* MyTouchID.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MyTouchID.app; sourceTree = BUILT_PRODUCTS_DIR; }; 96 | 5F4EBCBA1BE2C5D900252D33 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 97 | 5F4EBCBC1BE2C5D900252D33 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 98 | 5F4EBCBD1BE2C5D900252D33 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 99 | 5F4EBCBF1BE2C5D900252D33 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 100 | 5F4EBCC01BE2C5D900252D33 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 101 | 5F4EBCC51BE2C5D900252D33 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 102 | 5F4EBCC81BE2C5D900252D33 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 103 | 5F4EBCCA1BE2C5D900252D33 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 104 | 5F4EBCD01BE2C86F00252D33 /* MBProgressHUD.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MBProgressHUD.framework; path = Carthage/Build/iOS/MBProgressHUD.framework; sourceTree = ""; }; 105 | 5F4EBCD11BE2C86F00252D33 /* MBProgressHUD.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = MBProgressHUD.framework.dSYM; path = Carthage/Build/iOS/MBProgressHUD.framework.dSYM; sourceTree = ""; }; 106 | 5F4EBCD41BE2C93E00252D33 /* A0RegisterViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = A0RegisterViewController.h; sourceTree = ""; }; 107 | 5F4EBCD51BE2C93E00252D33 /* A0RegisterViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = A0RegisterViewController.m; sourceTree = ""; }; 108 | 5F4EBCD61BE2C93E00252D33 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; 109 | 5F4EBCD91BE2C9F900252D33 /* AFNetworking.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AFNetworking.framework; path = Carthage/Build/iOS/AFNetworking.framework; sourceTree = ""; }; 110 | 5F4EBCDA1BE2C9F900252D33 /* AFNetworking.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = AFNetworking.framework.dSYM; path = Carthage/Build/iOS/AFNetworking.framework.dSYM; sourceTree = ""; }; 111 | 5F4EBCE11BE2CAF400252D33 /* TouchIDAuthTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TouchIDAuthTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 112 | 5F4EBCE51BE2CAF400252D33 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 113 | 5F77FA321BE3AB0A00DBDE69 /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nimble.framework; path = Carthage/Build/iOS/Nimble.framework; sourceTree = ""; }; 114 | 5F77FA331BE3AB0A00DBDE69 /* Nimble.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = Nimble.framework.dSYM; path = Carthage/Build/iOS/Nimble.framework.dSYM; sourceTree = ""; }; 115 | 5F77FA341BE3AB0A00DBDE69 /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quick.framework; path = Carthage/Build/iOS/Quick.framework; sourceTree = ""; }; 116 | 5F77FA351BE3AB0A00DBDE69 /* Quick.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = Quick.framework.dSYM; path = Carthage/Build/iOS/Quick.framework.dSYM; sourceTree = ""; }; 117 | 5F77FA391BE3ADE600DBDE69 /* TouchIDAuthTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TouchIDAuthTests-Bridging-Header.h"; sourceTree = ""; }; 118 | 5F77FA3A1BE3ADE700DBDE69 /* A0RSAKeyExporterSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = A0RSAKeyExporterSpec.swift; sourceTree = ""; }; 119 | 5F77FA411BE3C30B00DBDE69 /* A0JWTSafeBase64Spec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = A0JWTSafeBase64Spec.swift; sourceTree = ""; }; 120 | 5F77FA441BE3DC6900DBDE69 /* A0TouchIDAuthenticationSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = A0TouchIDAuthenticationSpec.swift; sourceTree = ""; }; 121 | 5F77FA461BE3EAC200DBDE69 /* JWTDecode.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JWTDecode.framework; path = Carthage/Build/iOS/JWTDecode.framework; sourceTree = ""; }; 122 | /* End PBXFileReference section */ 123 | 124 | /* Begin PBXFrameworksBuildPhase section */ 125 | 5F4EBC871BE2AAEC00252D33 /* Frameworks */ = { 126 | isa = PBXFrameworksBuildPhase; 127 | buildActionMask = 2147483647; 128 | files = ( 129 | 5F4EBCAD1BE2AC9000252D33 /* LocalAuthentication.framework in Frameworks */, 130 | 5F4EBCAB1BE2AC8C00252D33 /* Security.framework in Frameworks */, 131 | 5F4EBCB11BE2C44A00252D33 /* SimpleKeychain.framework in Frameworks */, 132 | ); 133 | runOnlyForDeploymentPostprocessing = 0; 134 | }; 135 | 5F4EBCB41BE2C5D900252D33 /* Frameworks */ = { 136 | isa = PBXFrameworksBuildPhase; 137 | buildActionMask = 2147483647; 138 | files = ( 139 | 5F4EBCD21BE2C86F00252D33 /* MBProgressHUD.framework in Frameworks */, 140 | 5F4EBCDB1BE2C9F900252D33 /* AFNetworking.framework in Frameworks */, 141 | ); 142 | runOnlyForDeploymentPostprocessing = 0; 143 | }; 144 | 5F4EBCDE1BE2CAF400252D33 /* Frameworks */ = { 145 | isa = PBXFrameworksBuildPhase; 146 | buildActionMask = 2147483647; 147 | files = ( 148 | 5F77FA471BE3EAC200DBDE69 /* JWTDecode.framework in Frameworks */, 149 | 5F77FA431BE3C98700DBDE69 /* Security.framework in Frameworks */, 150 | 5F77FA371BE3AB4800DBDE69 /* Nimble.framework in Frameworks */, 151 | 5F77FA381BE3AB4800DBDE69 /* Quick.framework in Frameworks */, 152 | ); 153 | runOnlyForDeploymentPostprocessing = 0; 154 | }; 155 | /* End PBXFrameworksBuildPhase section */ 156 | 157 | /* Begin PBXGroup section */ 158 | 5F4EBC811BE2AAEC00252D33 = { 159 | isa = PBXGroup; 160 | children = ( 161 | 5F4EBC8D1BE2AAEC00252D33 /* TouchIDAuth */, 162 | 5F4EBCB81BE2C5D900252D33 /* MyTouchID */, 163 | 5F4EBCE21BE2CAF400252D33 /* TouchIDAuthTests */, 164 | 5F4EBC8C1BE2AAEC00252D33 /* Products */, 165 | 5F4EBCAE1BE2AC9500252D33 /* Frameworks */, 166 | ); 167 | sourceTree = ""; 168 | }; 169 | 5F4EBC8C1BE2AAEC00252D33 /* Products */ = { 170 | isa = PBXGroup; 171 | children = ( 172 | 5F4EBC8B1BE2AAEC00252D33 /* TouchIDAuth.framework */, 173 | 5F4EBCB71BE2C5D900252D33 /* MyTouchID.app */, 174 | 5F4EBCE11BE2CAF400252D33 /* TouchIDAuthTests.xctest */, 175 | ); 176 | name = Products; 177 | sourceTree = ""; 178 | }; 179 | 5F4EBC8D1BE2AAEC00252D33 /* TouchIDAuth */ = { 180 | isa = PBXGroup; 181 | children = ( 182 | 5F4EBC961BE2AC5E00252D33 /* A0JWTBuilder.h */, 183 | 5F4EBC971BE2AC5E00252D33 /* A0JWTBuilder.m */, 184 | 5F4EBC981BE2AC5E00252D33 /* A0RSAKeyExporter.h */, 185 | 5F4EBC991BE2AC5E00252D33 /* A0RSAKeyExporter.m */, 186 | 5F4EBC9A1BE2AC5E00252D33 /* A0TouchID.h */, 187 | 5F4EBC9B1BE2AC5E00252D33 /* A0TouchID.m */, 188 | 5F4EBC9C1BE2AC5E00252D33 /* A0TouchIDAuthentication.h */, 189 | 5F4EBC9D1BE2AC5E00252D33 /* A0TouchIDAuthentication.m */, 190 | 5F4EBC9E1BE2AC5E00252D33 /* NSData+A0JWTSafeBase64.h */, 191 | 5F4EBC9F1BE2AC5E00252D33 /* NSData+A0JWTSafeBase64.m */, 192 | 5F4EBC8E1BE2AAEC00252D33 /* TouchIDAuth.h */, 193 | 5F4EBC901BE2AAEC00252D33 /* Info.plist */, 194 | ); 195 | path = TouchIDAuth; 196 | sourceTree = ""; 197 | }; 198 | 5F4EBCAE1BE2AC9500252D33 /* Frameworks */ = { 199 | isa = PBXGroup; 200 | children = ( 201 | 5F77FA461BE3EAC200DBDE69 /* JWTDecode.framework */, 202 | 5F77FA321BE3AB0A00DBDE69 /* Nimble.framework */, 203 | 5F77FA331BE3AB0A00DBDE69 /* Nimble.framework.dSYM */, 204 | 5F77FA341BE3AB0A00DBDE69 /* Quick.framework */, 205 | 5F77FA351BE3AB0A00DBDE69 /* Quick.framework.dSYM */, 206 | 5F4EBCD91BE2C9F900252D33 /* AFNetworking.framework */, 207 | 5F4EBCDA1BE2C9F900252D33 /* AFNetworking.framework.dSYM */, 208 | 5F4EBCD01BE2C86F00252D33 /* MBProgressHUD.framework */, 209 | 5F4EBCD11BE2C86F00252D33 /* MBProgressHUD.framework.dSYM */, 210 | 5F4EBCAF1BE2C44A00252D33 /* SimpleKeychain.framework */, 211 | 5F4EBCB01BE2C44A00252D33 /* SimpleKeychain.framework.dSYM */, 212 | 5F4EBCAC1BE2AC9000252D33 /* LocalAuthentication.framework */, 213 | 5F4EBCAA1BE2AC8C00252D33 /* Security.framework */, 214 | ); 215 | name = Frameworks; 216 | sourceTree = ""; 217 | }; 218 | 5F4EBCB81BE2C5D900252D33 /* MyTouchID */ = { 219 | isa = PBXGroup; 220 | children = ( 221 | 5F4EBCD41BE2C93E00252D33 /* A0RegisterViewController.h */, 222 | 5F4EBCD51BE2C93E00252D33 /* A0RegisterViewController.m */, 223 | 5F4EBCD61BE2C93E00252D33 /* Main.storyboard */, 224 | 5F4EBCBC1BE2C5D900252D33 /* AppDelegate.h */, 225 | 5F4EBCBD1BE2C5D900252D33 /* AppDelegate.m */, 226 | 5F4EBCBF1BE2C5D900252D33 /* ViewController.h */, 227 | 5F4EBCC01BE2C5D900252D33 /* ViewController.m */, 228 | 5F4EBCC51BE2C5D900252D33 /* Assets.xcassets */, 229 | 5F4EBCC71BE2C5D900252D33 /* LaunchScreen.storyboard */, 230 | 5F4EBCCA1BE2C5D900252D33 /* Info.plist */, 231 | 5F4EBCB91BE2C5D900252D33 /* Supporting Files */, 232 | ); 233 | path = MyTouchID; 234 | sourceTree = ""; 235 | }; 236 | 5F4EBCB91BE2C5D900252D33 /* Supporting Files */ = { 237 | isa = PBXGroup; 238 | children = ( 239 | 5F4EBCBA1BE2C5D900252D33 /* main.m */, 240 | ); 241 | name = "Supporting Files"; 242 | sourceTree = ""; 243 | }; 244 | 5F4EBCE21BE2CAF400252D33 /* TouchIDAuthTests */ = { 245 | isa = PBXGroup; 246 | children = ( 247 | 5F4EBCE51BE2CAF400252D33 /* Info.plist */, 248 | 5F77FA3A1BE3ADE700DBDE69 /* A0RSAKeyExporterSpec.swift */, 249 | 5F77FA391BE3ADE600DBDE69 /* TouchIDAuthTests-Bridging-Header.h */, 250 | 5F77FA411BE3C30B00DBDE69 /* A0JWTSafeBase64Spec.swift */, 251 | 5F77FA441BE3DC6900DBDE69 /* A0TouchIDAuthenticationSpec.swift */, 252 | ); 253 | path = TouchIDAuthTests; 254 | sourceTree = ""; 255 | }; 256 | /* End PBXGroup section */ 257 | 258 | /* Begin PBXHeadersBuildPhase section */ 259 | 5F4EBC881BE2AAEC00252D33 /* Headers */ = { 260 | isa = PBXHeadersBuildPhase; 261 | buildActionMask = 2147483647; 262 | files = ( 263 | 5F4EBCA61BE2AC5E00252D33 /* A0TouchIDAuthentication.h in Headers */, 264 | 5F4EBC8F1BE2AAEC00252D33 /* TouchIDAuth.h in Headers */, 265 | 5F4EBCA81BE2AC5E00252D33 /* NSData+A0JWTSafeBase64.h in Headers */, 266 | 5F4EBCA01BE2AC5E00252D33 /* A0JWTBuilder.h in Headers */, 267 | 5F4EBCA41BE2AC5E00252D33 /* A0TouchID.h in Headers */, 268 | 5F4EBCA21BE2AC5E00252D33 /* A0RSAKeyExporter.h in Headers */, 269 | ); 270 | runOnlyForDeploymentPostprocessing = 0; 271 | }; 272 | /* End PBXHeadersBuildPhase section */ 273 | 274 | /* Begin PBXNativeTarget section */ 275 | 5F4EBC8A1BE2AAEC00252D33 /* TouchIDAuth-iOS */ = { 276 | isa = PBXNativeTarget; 277 | buildConfigurationList = 5F4EBC931BE2AAEC00252D33 /* Build configuration list for PBXNativeTarget "TouchIDAuth-iOS" */; 278 | buildPhases = ( 279 | 5F4EBC861BE2AAEC00252D33 /* Sources */, 280 | 5F4EBC871BE2AAEC00252D33 /* Frameworks */, 281 | 5F4EBC881BE2AAEC00252D33 /* Headers */, 282 | 5F4EBC891BE2AAEC00252D33 /* Resources */, 283 | ); 284 | buildRules = ( 285 | ); 286 | dependencies = ( 287 | ); 288 | name = "TouchIDAuth-iOS"; 289 | productName = TouchIDAuth; 290 | productReference = 5F4EBC8B1BE2AAEC00252D33 /* TouchIDAuth.framework */; 291 | productType = "com.apple.product-type.framework"; 292 | }; 293 | 5F4EBCB61BE2C5D900252D33 /* MyTouchID */ = { 294 | isa = PBXNativeTarget; 295 | buildConfigurationList = 5F4EBCCB1BE2C5D900252D33 /* Build configuration list for PBXNativeTarget "MyTouchID" */; 296 | buildPhases = ( 297 | 5F4EBCB31BE2C5D900252D33 /* Sources */, 298 | 5F4EBCB41BE2C5D900252D33 /* Frameworks */, 299 | 5F4EBCB51BE2C5D900252D33 /* Resources */, 300 | 5F77FA3C1BE3B11F00DBDE69 /* Carthage */, 301 | ); 302 | buildRules = ( 303 | ); 304 | dependencies = ( 305 | 5F4EBCCF1BE2C5E100252D33 /* PBXTargetDependency */, 306 | ); 307 | name = MyTouchID; 308 | productName = MyTouchID; 309 | productReference = 5F4EBCB71BE2C5D900252D33 /* MyTouchID.app */; 310 | productType = "com.apple.product-type.application"; 311 | }; 312 | 5F4EBCE01BE2CAF400252D33 /* TouchIDAuthTests */ = { 313 | isa = PBXNativeTarget; 314 | buildConfigurationList = 5F4EBCE91BE2CAF400252D33 /* Build configuration list for PBXNativeTarget "TouchIDAuthTests" */; 315 | buildPhases = ( 316 | 5F4EBCDD1BE2CAF400252D33 /* Sources */, 317 | 5F4EBCDE1BE2CAF400252D33 /* Frameworks */, 318 | 5F4EBCDF1BE2CAF400252D33 /* Resources */, 319 | 5F77FA3D1BE3B21C00DBDE69 /* CopyFiles */, 320 | ); 321 | buildRules = ( 322 | ); 323 | dependencies = ( 324 | 5F4EBCED1BE2CAF900252D33 /* PBXTargetDependency */, 325 | ); 326 | name = TouchIDAuthTests; 327 | productName = TouchIDAuthTests; 328 | productReference = 5F4EBCE11BE2CAF400252D33 /* TouchIDAuthTests.xctest */; 329 | productType = "com.apple.product-type.bundle.unit-test"; 330 | }; 331 | /* End PBXNativeTarget section */ 332 | 333 | /* Begin PBXProject section */ 334 | 5F4EBC821BE2AAEC00252D33 /* Project object */ = { 335 | isa = PBXProject; 336 | attributes = { 337 | LastUpgradeCheck = 0710; 338 | ORGANIZATIONNAME = Auth0; 339 | TargetAttributes = { 340 | 5F4EBC8A1BE2AAEC00252D33 = { 341 | CreatedOnToolsVersion = 7.1; 342 | }; 343 | 5F4EBCB61BE2C5D900252D33 = { 344 | CreatedOnToolsVersion = 7.1; 345 | }; 346 | 5F4EBCE01BE2CAF400252D33 = { 347 | CreatedOnToolsVersion = 7.1; 348 | TestTargetID = 5F4EBCB61BE2C5D900252D33; 349 | }; 350 | }; 351 | }; 352 | buildConfigurationList = 5F4EBC851BE2AAEC00252D33 /* Build configuration list for PBXProject "TouchIDAuth" */; 353 | compatibilityVersion = "Xcode 3.2"; 354 | developmentRegion = English; 355 | hasScannedForEncodings = 0; 356 | knownRegions = ( 357 | en, 358 | Base, 359 | ); 360 | mainGroup = 5F4EBC811BE2AAEC00252D33; 361 | productRefGroup = 5F4EBC8C1BE2AAEC00252D33 /* Products */; 362 | projectDirPath = ""; 363 | projectRoot = ""; 364 | targets = ( 365 | 5F4EBC8A1BE2AAEC00252D33 /* TouchIDAuth-iOS */, 366 | 5F4EBCB61BE2C5D900252D33 /* MyTouchID */, 367 | 5F4EBCE01BE2CAF400252D33 /* TouchIDAuthTests */, 368 | ); 369 | }; 370 | /* End PBXProject section */ 371 | 372 | /* Begin PBXResourcesBuildPhase section */ 373 | 5F4EBC891BE2AAEC00252D33 /* Resources */ = { 374 | isa = PBXResourcesBuildPhase; 375 | buildActionMask = 2147483647; 376 | files = ( 377 | ); 378 | runOnlyForDeploymentPostprocessing = 0; 379 | }; 380 | 5F4EBCB51BE2C5D900252D33 /* Resources */ = { 381 | isa = PBXResourcesBuildPhase; 382 | buildActionMask = 2147483647; 383 | files = ( 384 | 5F4EBCD81BE2C93E00252D33 /* Main.storyboard in Resources */, 385 | 5F4EBCC91BE2C5D900252D33 /* LaunchScreen.storyboard in Resources */, 386 | 5F4EBCC61BE2C5D900252D33 /* Assets.xcassets in Resources */, 387 | ); 388 | runOnlyForDeploymentPostprocessing = 0; 389 | }; 390 | 5F4EBCDF1BE2CAF400252D33 /* Resources */ = { 391 | isa = PBXResourcesBuildPhase; 392 | buildActionMask = 2147483647; 393 | files = ( 394 | ); 395 | runOnlyForDeploymentPostprocessing = 0; 396 | }; 397 | /* End PBXResourcesBuildPhase section */ 398 | 399 | /* Begin PBXShellScriptBuildPhase section */ 400 | 5F77FA3C1BE3B11F00DBDE69 /* Carthage */ = { 401 | isa = PBXShellScriptBuildPhase; 402 | buildActionMask = 2147483647; 403 | files = ( 404 | ); 405 | inputPaths = ( 406 | "$(SRCROOT)/Carthage/Build/iOS/AFNetworking.framework", 407 | "$(SRCROOT)/Carthage/Build/iOS/MBProgressHUD.framework", 408 | "$(SRCROOT)/Carthage/Build/iOS/SimpleKeychain.framework", 409 | ); 410 | name = Carthage; 411 | outputPaths = ( 412 | ); 413 | runOnlyForDeploymentPostprocessing = 0; 414 | shellPath = /bin/sh; 415 | shellScript = "/usr/local/bin/carthage copy-frameworks"; 416 | }; 417 | /* End PBXShellScriptBuildPhase section */ 418 | 419 | /* Begin PBXSourcesBuildPhase section */ 420 | 5F4EBC861BE2AAEC00252D33 /* Sources */ = { 421 | isa = PBXSourcesBuildPhase; 422 | buildActionMask = 2147483647; 423 | files = ( 424 | 5F4EBCA51BE2AC5E00252D33 /* A0TouchID.m in Sources */, 425 | 5F4EBCA71BE2AC5E00252D33 /* A0TouchIDAuthentication.m in Sources */, 426 | 5F4EBCA91BE2AC5E00252D33 /* NSData+A0JWTSafeBase64.m in Sources */, 427 | 5F4EBCA31BE2AC5E00252D33 /* A0RSAKeyExporter.m in Sources */, 428 | 5F4EBCA11BE2AC5E00252D33 /* A0JWTBuilder.m in Sources */, 429 | ); 430 | runOnlyForDeploymentPostprocessing = 0; 431 | }; 432 | 5F4EBCB31BE2C5D900252D33 /* Sources */ = { 433 | isa = PBXSourcesBuildPhase; 434 | buildActionMask = 2147483647; 435 | files = ( 436 | 5F4EBCC11BE2C5D900252D33 /* ViewController.m in Sources */, 437 | 5F4EBCBE1BE2C5D900252D33 /* AppDelegate.m in Sources */, 438 | 5F4EBCD71BE2C93E00252D33 /* A0RegisterViewController.m in Sources */, 439 | 5F4EBCBB1BE2C5D900252D33 /* main.m in Sources */, 440 | ); 441 | runOnlyForDeploymentPostprocessing = 0; 442 | }; 443 | 5F4EBCDD1BE2CAF400252D33 /* Sources */ = { 444 | isa = PBXSourcesBuildPhase; 445 | buildActionMask = 2147483647; 446 | files = ( 447 | 5F77FA3B1BE3ADE700DBDE69 /* A0RSAKeyExporterSpec.swift in Sources */, 448 | 5F77FA421BE3C30B00DBDE69 /* A0JWTSafeBase64Spec.swift in Sources */, 449 | 5F77FA451BE3DC6900DBDE69 /* A0TouchIDAuthenticationSpec.swift in Sources */, 450 | ); 451 | runOnlyForDeploymentPostprocessing = 0; 452 | }; 453 | /* End PBXSourcesBuildPhase section */ 454 | 455 | /* Begin PBXTargetDependency section */ 456 | 5F4EBCCF1BE2C5E100252D33 /* PBXTargetDependency */ = { 457 | isa = PBXTargetDependency; 458 | target = 5F4EBC8A1BE2AAEC00252D33 /* TouchIDAuth-iOS */; 459 | targetProxy = 5F4EBCCE1BE2C5E100252D33 /* PBXContainerItemProxy */; 460 | }; 461 | 5F4EBCED1BE2CAF900252D33 /* PBXTargetDependency */ = { 462 | isa = PBXTargetDependency; 463 | target = 5F4EBCB61BE2C5D900252D33 /* MyTouchID */; 464 | targetProxy = 5F4EBCEC1BE2CAF900252D33 /* PBXContainerItemProxy */; 465 | }; 466 | /* End PBXTargetDependency section */ 467 | 468 | /* Begin PBXVariantGroup section */ 469 | 5F4EBCC71BE2C5D900252D33 /* LaunchScreen.storyboard */ = { 470 | isa = PBXVariantGroup; 471 | children = ( 472 | 5F4EBCC81BE2C5D900252D33 /* Base */, 473 | ); 474 | name = LaunchScreen.storyboard; 475 | sourceTree = ""; 476 | }; 477 | /* End PBXVariantGroup section */ 478 | 479 | /* Begin XCBuildConfiguration section */ 480 | 5F4EBC911BE2AAEC00252D33 /* Debug */ = { 481 | isa = XCBuildConfiguration; 482 | buildSettings = { 483 | ALWAYS_SEARCH_USER_PATHS = NO; 484 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 485 | CLANG_CXX_LIBRARY = "libc++"; 486 | CLANG_ENABLE_MODULES = YES; 487 | CLANG_ENABLE_OBJC_ARC = YES; 488 | CLANG_WARN_BOOL_CONVERSION = YES; 489 | CLANG_WARN_CONSTANT_CONVERSION = YES; 490 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 491 | CLANG_WARN_EMPTY_BODY = YES; 492 | CLANG_WARN_ENUM_CONVERSION = YES; 493 | CLANG_WARN_INT_CONVERSION = YES; 494 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 495 | CLANG_WARN_UNREACHABLE_CODE = YES; 496 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 497 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 498 | COPY_PHASE_STRIP = NO; 499 | CURRENT_PROJECT_VERSION = 1; 500 | DEBUG_INFORMATION_FORMAT = dwarf; 501 | ENABLE_STRICT_OBJC_MSGSEND = YES; 502 | ENABLE_TESTABILITY = YES; 503 | GCC_C_LANGUAGE_STANDARD = gnu99; 504 | GCC_DYNAMIC_NO_PIC = NO; 505 | GCC_NO_COMMON_BLOCKS = YES; 506 | GCC_OPTIMIZATION_LEVEL = 0; 507 | GCC_PREPROCESSOR_DEFINITIONS = ( 508 | "DEBUG=1", 509 | "$(inherited)", 510 | ); 511 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 512 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 513 | GCC_WARN_UNDECLARED_SELECTOR = YES; 514 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 515 | GCC_WARN_UNUSED_FUNCTION = YES; 516 | GCC_WARN_UNUSED_VARIABLE = YES; 517 | IPHONEOS_DEPLOYMENT_TARGET = 9.1; 518 | MTL_ENABLE_DEBUG_INFO = YES; 519 | ONLY_ACTIVE_ARCH = YES; 520 | SDKROOT = iphoneos; 521 | TARGETED_DEVICE_FAMILY = "1,2"; 522 | VERSIONING_SYSTEM = "apple-generic"; 523 | VERSION_INFO_PREFIX = ""; 524 | }; 525 | name = Debug; 526 | }; 527 | 5F4EBC921BE2AAEC00252D33 /* Release */ = { 528 | isa = XCBuildConfiguration; 529 | buildSettings = { 530 | ALWAYS_SEARCH_USER_PATHS = NO; 531 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 532 | CLANG_CXX_LIBRARY = "libc++"; 533 | CLANG_ENABLE_MODULES = YES; 534 | CLANG_ENABLE_OBJC_ARC = YES; 535 | CLANG_WARN_BOOL_CONVERSION = YES; 536 | CLANG_WARN_CONSTANT_CONVERSION = YES; 537 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 538 | CLANG_WARN_EMPTY_BODY = YES; 539 | CLANG_WARN_ENUM_CONVERSION = YES; 540 | CLANG_WARN_INT_CONVERSION = YES; 541 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 542 | CLANG_WARN_UNREACHABLE_CODE = YES; 543 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 544 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 545 | COPY_PHASE_STRIP = NO; 546 | CURRENT_PROJECT_VERSION = 1; 547 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 548 | ENABLE_NS_ASSERTIONS = NO; 549 | ENABLE_STRICT_OBJC_MSGSEND = YES; 550 | GCC_C_LANGUAGE_STANDARD = gnu99; 551 | GCC_NO_COMMON_BLOCKS = YES; 552 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 553 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 554 | GCC_WARN_UNDECLARED_SELECTOR = YES; 555 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 556 | GCC_WARN_UNUSED_FUNCTION = YES; 557 | GCC_WARN_UNUSED_VARIABLE = YES; 558 | IPHONEOS_DEPLOYMENT_TARGET = 9.1; 559 | MTL_ENABLE_DEBUG_INFO = NO; 560 | SDKROOT = iphoneos; 561 | TARGETED_DEVICE_FAMILY = "1,2"; 562 | VALIDATE_PRODUCT = YES; 563 | VERSIONING_SYSTEM = "apple-generic"; 564 | VERSION_INFO_PREFIX = ""; 565 | }; 566 | name = Release; 567 | }; 568 | 5F4EBC941BE2AAEC00252D33 /* Debug */ = { 569 | isa = XCBuildConfiguration; 570 | buildSettings = { 571 | DEFINES_MODULE = YES; 572 | DYLIB_COMPATIBILITY_VERSION = 1; 573 | DYLIB_CURRENT_VERSION = 1; 574 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 575 | FRAMEWORK_SEARCH_PATHS = ( 576 | "$(inherited)", 577 | "$(PROJECT_DIR)/Carthage/Build/iOS", 578 | ); 579 | INFOPLIST_FILE = TouchIDAuth/Info.plist; 580 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 581 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 582 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 583 | PRODUCT_BUNDLE_IDENTIFIER = com.auth0.TouchIDAuth; 584 | PRODUCT_NAME = TouchIDAuth; 585 | SKIP_INSTALL = YES; 586 | }; 587 | name = Debug; 588 | }; 589 | 5F4EBC951BE2AAEC00252D33 /* Release */ = { 590 | isa = XCBuildConfiguration; 591 | buildSettings = { 592 | DEFINES_MODULE = YES; 593 | DYLIB_COMPATIBILITY_VERSION = 1; 594 | DYLIB_CURRENT_VERSION = 1; 595 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 596 | FRAMEWORK_SEARCH_PATHS = ( 597 | "$(inherited)", 598 | "$(PROJECT_DIR)/Carthage/Build/iOS", 599 | ); 600 | INFOPLIST_FILE = TouchIDAuth/Info.plist; 601 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 602 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 603 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 604 | PRODUCT_BUNDLE_IDENTIFIER = com.auth0.TouchIDAuth; 605 | PRODUCT_NAME = TouchIDAuth; 606 | SKIP_INSTALL = YES; 607 | }; 608 | name = Release; 609 | }; 610 | 5F4EBCCC1BE2C5D900252D33 /* Debug */ = { 611 | isa = XCBuildConfiguration; 612 | buildSettings = { 613 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 614 | FRAMEWORK_SEARCH_PATHS = ( 615 | "$(inherited)", 616 | "$(PROJECT_DIR)/Carthage/Build/iOS", 617 | ); 618 | INFOPLIST_FILE = MyTouchID/Info.plist; 619 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 620 | PRODUCT_BUNDLE_IDENTIFIER = com.auth0.MyTouchID; 621 | PRODUCT_NAME = "$(TARGET_NAME)"; 622 | }; 623 | name = Debug; 624 | }; 625 | 5F4EBCCD1BE2C5D900252D33 /* Release */ = { 626 | isa = XCBuildConfiguration; 627 | buildSettings = { 628 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 629 | FRAMEWORK_SEARCH_PATHS = ( 630 | "$(inherited)", 631 | "$(PROJECT_DIR)/Carthage/Build/iOS", 632 | ); 633 | INFOPLIST_FILE = MyTouchID/Info.plist; 634 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 635 | PRODUCT_BUNDLE_IDENTIFIER = com.auth0.MyTouchID; 636 | PRODUCT_NAME = "$(TARGET_NAME)"; 637 | }; 638 | name = Release; 639 | }; 640 | 5F4EBCEA1BE2CAF400252D33 /* Debug */ = { 641 | isa = XCBuildConfiguration; 642 | buildSettings = { 643 | CLANG_ENABLE_MODULES = YES; 644 | FRAMEWORK_SEARCH_PATHS = ( 645 | "$(inherited)", 646 | "$(PROJECT_DIR)/Carthage/Build/iOS", 647 | ); 648 | INFOPLIST_FILE = TouchIDAuthTests/Info.plist; 649 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 650 | PRODUCT_BUNDLE_IDENTIFIER = com.auth0.TouchIDAuthTests; 651 | PRODUCT_NAME = "$(TARGET_NAME)"; 652 | SWIFT_OBJC_BRIDGING_HEADER = "TouchIDAuthTests/TouchIDAuthTests-Bridging-Header.h"; 653 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 654 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MyTouchID.app/MyTouchID"; 655 | }; 656 | name = Debug; 657 | }; 658 | 5F4EBCEB1BE2CAF400252D33 /* Release */ = { 659 | isa = XCBuildConfiguration; 660 | buildSettings = { 661 | CLANG_ENABLE_MODULES = YES; 662 | FRAMEWORK_SEARCH_PATHS = ( 663 | "$(inherited)", 664 | "$(PROJECT_DIR)/Carthage/Build/iOS", 665 | ); 666 | INFOPLIST_FILE = TouchIDAuthTests/Info.plist; 667 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 668 | PRODUCT_BUNDLE_IDENTIFIER = com.auth0.TouchIDAuthTests; 669 | PRODUCT_NAME = "$(TARGET_NAME)"; 670 | SWIFT_OBJC_BRIDGING_HEADER = "TouchIDAuthTests/TouchIDAuthTests-Bridging-Header.h"; 671 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MyTouchID.app/MyTouchID"; 672 | }; 673 | name = Release; 674 | }; 675 | /* End XCBuildConfiguration section */ 676 | 677 | /* Begin XCConfigurationList section */ 678 | 5F4EBC851BE2AAEC00252D33 /* Build configuration list for PBXProject "TouchIDAuth" */ = { 679 | isa = XCConfigurationList; 680 | buildConfigurations = ( 681 | 5F4EBC911BE2AAEC00252D33 /* Debug */, 682 | 5F4EBC921BE2AAEC00252D33 /* Release */, 683 | ); 684 | defaultConfigurationIsVisible = 0; 685 | defaultConfigurationName = Release; 686 | }; 687 | 5F4EBC931BE2AAEC00252D33 /* Build configuration list for PBXNativeTarget "TouchIDAuth-iOS" */ = { 688 | isa = XCConfigurationList; 689 | buildConfigurations = ( 690 | 5F4EBC941BE2AAEC00252D33 /* Debug */, 691 | 5F4EBC951BE2AAEC00252D33 /* Release */, 692 | ); 693 | defaultConfigurationIsVisible = 0; 694 | defaultConfigurationName = Release; 695 | }; 696 | 5F4EBCCB1BE2C5D900252D33 /* Build configuration list for PBXNativeTarget "MyTouchID" */ = { 697 | isa = XCConfigurationList; 698 | buildConfigurations = ( 699 | 5F4EBCCC1BE2C5D900252D33 /* Debug */, 700 | 5F4EBCCD1BE2C5D900252D33 /* Release */, 701 | ); 702 | defaultConfigurationIsVisible = 0; 703 | defaultConfigurationName = Release; 704 | }; 705 | 5F4EBCE91BE2CAF400252D33 /* Build configuration list for PBXNativeTarget "TouchIDAuthTests" */ = { 706 | isa = XCConfigurationList; 707 | buildConfigurations = ( 708 | 5F4EBCEA1BE2CAF400252D33 /* Debug */, 709 | 5F4EBCEB1BE2CAF400252D33 /* Release */, 710 | ); 711 | defaultConfigurationIsVisible = 0; 712 | defaultConfigurationName = Release; 713 | }; 714 | /* End XCConfigurationList section */ 715 | }; 716 | rootObject = 5F4EBC821BE2AAEC00252D33 /* Project object */; 717 | } 718 | -------------------------------------------------------------------------------- /TouchIDAuth.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /TouchIDAuth.xcodeproj/xcshareddata/xcschemes/TouchIDAuth-iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /TouchIDAuth/A0JWTBuilder.h: -------------------------------------------------------------------------------- 1 | // A0JWTBuilder.h 2 | // 3 | // Copyright (c) 2015 Auth0 (http://auth0.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import 24 | 25 | /** 26 | * Types of Sign for a JWT 27 | */ 28 | typedef NS_ENUM(NSUInteger, A0JWTSignMethod){ 29 | /** 30 | * RS256 (RSA with SHA256). 31 | */ 32 | A0JWTSignMethodRS256 = 0, 33 | }; 34 | 35 | /** 36 | * Builder of JWT. 37 | */ 38 | @interface A0JWTBuilder : NSObject 39 | 40 | /** 41 | * Sets the JWT payload 42 | * 43 | * @param payload new payload 44 | * 45 | * @return the instance itself. 46 | */ 47 | - (instancetype)setJWTPayload:(NSDictionary *)payload; 48 | 49 | /** 50 | * Signs the current payload and generates the header accordingly. For the moment only RS256 is supported. 51 | * 52 | * @param signMethod sign method used. 53 | * @param keyOrSecret key or secret 54 | * 55 | * @return then instance itself. 56 | */ 57 | - (instancetype)signWithMethod:(A0JWTSignMethod)signMethod andKeyOrSecret:(id)keyOrSecret; 58 | 59 | /** 60 | * Returns the JWT. If it's not signed it will return nil. 61 | * 62 | * @return the JWT or nil. 63 | */ 64 | - (NSString *)jwt; 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /TouchIDAuth/A0JWTBuilder.m: -------------------------------------------------------------------------------- 1 | // A0JWTBuilder.m 2 | // 3 | // Copyright (c) 2015 Auth0 (http://auth0.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import "A0JWTBuilder.h" 24 | #import "NSData+A0JWTSafeBase64.h" 25 | #import 26 | 27 | #define kHashLength CC_SHA256_DIGEST_LENGTH 28 | 29 | @interface A0JWTBuilder () 30 | @property (strong, nonatomic) NSString *header; 31 | @property (strong, nonatomic) NSString *payload; 32 | @property (strong, nonatomic) NSString *signature; 33 | @end 34 | 35 | @implementation A0JWTBuilder 36 | 37 | - (instancetype)setJWTPayload:(NSDictionary *)payload { 38 | self.payload = [[NSJSONSerialization dataWithJSONObject:payload options:0 error:nil] a0_jwtSafeBase64String]; 39 | return self; 40 | } 41 | 42 | - (instancetype)signWithMethod:(A0JWTSignMethod)signMethod andKeyOrSecret:(id)keyOrSecret { 43 | NSDictionary *header = @{ 44 | @"alg": @"RS256", 45 | @"typ": @"JWT", 46 | }; 47 | self.header = [[NSJSONSerialization dataWithJSONObject:header options:0 error:nil] a0_jwtSafeBase64String]; 48 | NSString *unsingedJWT = [[self.header stringByAppendingString:@"."] stringByAppendingString:self.payload]; 49 | SecKeyRef privateKeyRef = (__bridge SecKeyRef)(keyOrSecret); 50 | NSData *signature; 51 | if (privateKeyRef) { 52 | size_t signatureSize = SecKeyGetBlockSize(privateKeyRef); 53 | uint8_t *signatureBytes = malloc(signatureSize * sizeof(uint8_t)); 54 | memset(signatureBytes, 0x0, signatureSize); 55 | NSData *hashedJWT = [self SHA256OfValue:unsingedJWT]; 56 | OSStatus status = SecKeyRawSign(privateKeyRef, kSecPaddingPKCS1SHA256, [hashedJWT bytes], kHashLength, signatureBytes, &signatureSize); 57 | if (status == errSecSuccess) { 58 | signature = [NSData dataWithBytes:signatureBytes length:signatureSize]; 59 | } 60 | CFRelease(privateKeyRef); 61 | if (signatureBytes) { 62 | free(signatureBytes); 63 | } 64 | } 65 | self.signature = [signature a0_jwtSafeBase64String]; 66 | return self; 67 | } 68 | 69 | - (NSString *)jwt { 70 | return [@[self.header, self.payload, self.signature] componentsJoinedByString:@"."]; 71 | } 72 | 73 | - (NSData *)SHA256OfValue:(NSString *)value { 74 | CC_SHA256_CTX ctx; 75 | 76 | uint8_t * hashBytes = malloc(CC_SHA256_DIGEST_LENGTH * sizeof(uint8_t)); 77 | memset(hashBytes, 0x0, CC_SHA256_DIGEST_LENGTH); 78 | 79 | NSData *valueData = [value dataUsingEncoding:NSUTF8StringEncoding]; 80 | 81 | CC_SHA256_Init(&ctx); 82 | CC_SHA256_Update(&ctx, [valueData bytes], (CC_LONG)[valueData length]); 83 | CC_SHA256_Final(hashBytes, &ctx); 84 | 85 | NSData *hash = [NSData dataWithBytes:hashBytes length:CC_SHA256_DIGEST_LENGTH]; 86 | 87 | if (hashBytes) { 88 | free(hashBytes); 89 | } 90 | 91 | return hash; 92 | } 93 | 94 | @end 95 | -------------------------------------------------------------------------------- /TouchIDAuth/A0RSAKeyExporter.h: -------------------------------------------------------------------------------- 1 | // A0RSAKeyExporter.h 2 | // 3 | // Copyright (c) 2015 Auth0 (http://auth0.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import 24 | 25 | /** 26 | * Exports a keys in RSA Public key format. 27 | */ 28 | @interface A0RSAKeyExporter : NSObject 29 | 30 | - (nullable NSData *)exportPublicKey:(nonnull NSData *)keyData; 31 | - (nonnull NSString *)fingerprintOfKey:(nonnull NSData *)keyData; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /TouchIDAuth/A0RSAKeyExporter.m: -------------------------------------------------------------------------------- 1 | // A0RSAKeyExporter.m 2 | // 3 | // Copyright (c) 2015 Auth0 (http://auth0.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import "A0RSAKeyExporter.h" 24 | 25 | #import 26 | 27 | @implementation A0RSAKeyExporter 28 | 29 | - (NSData *)exportPublicKey:(NSData *)keyData { 30 | if (keyData) { 31 | NSMutableString *rsaKey = [[NSMutableString alloc] init]; 32 | [rsaKey appendString:@"-----BEGIN RSA PUBLIC KEY-----"]; 33 | [rsaKey appendString:@"\n"]; 34 | [rsaKey appendString:[keyData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength|NSDataBase64EncodingEndLineWithCarriageReturn]]; 35 | [rsaKey appendString:@"\n"]; 36 | [rsaKey appendString:@"-----END RSA PUBLIC KEY-----"]; 37 | return [rsaKey dataUsingEncoding:NSUTF8StringEncoding]; 38 | } else { 39 | return nil; 40 | } 41 | } 42 | 43 | - (NSString *)fingerprintOfKey:(NSData *)keyData { 44 | unsigned char result[16]; 45 | CC_MD5(keyData.bytes, (CC_LONG)keyData.length, result); 46 | 47 | return [NSString stringWithFormat: 48 | @"%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", 49 | result[0], result[1], result[2], result[3], 50 | result[4], result[5], result[6], result[7], 51 | result[8], result[9], result[10], result[11], 52 | result[12], result[13], result[14], result[15] 53 | ]; 54 | } 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /TouchIDAuth/A0TouchID.h: -------------------------------------------------------------------------------- 1 | // A0TouchIDAvailability.h 2 | // 3 | // Copyright (c) 2015 Auth0 (http://auth0.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import 24 | 25 | /** 26 | * Checks for TouchID availability in the device 27 | */ 28 | @interface A0TouchID : NSObject 29 | 30 | @property (readonly, nonatomic, getter=isAvailable) BOOL available; 31 | 32 | /** 33 | * Validates the user presence with TouchID. 34 | * 35 | * @param completionBlock block called when evaluation is completed 36 | */ 37 | - (void)validateWithCompletion:(void(^)(BOOL success, NSError *error))completionBlock; 38 | 39 | /** 40 | * Validates the user presence with TouchID. 41 | * 42 | * @param completionBlock block called when evaluation is completed 43 | * @param reason localized reason displayed in TouchID prompt. 44 | */ 45 | - (void)validateWithCompletion:(void(^)(BOOL success, NSError *error))completionBlock localizedReason:(NSString *)reason; 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /TouchIDAuth/A0TouchID.m: -------------------------------------------------------------------------------- 1 | // A0TouchIDAvailability.m 2 | // 3 | // Copyright (c) 2015 Auth0 (http://auth0.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import "A0TouchID.h" 24 | 25 | #import 26 | #ifdef __IPHONE_8_0 27 | #import 28 | #endif 29 | 30 | #define kTouchIDEntryKey @"auth0-touchid-flag" 31 | 32 | @implementation A0TouchID 33 | 34 | - (BOOL)isAvailable { 35 | #if TARGET_IPHONE_SIMULATOR 36 | return YES; 37 | #elif defined __IPHONE_8_0 38 | if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_7_1) { //iOS 8 39 | LAContext *context = [[LAContext alloc] init]; 40 | NSError *error; 41 | BOOL available = [context canEvaluatePolicy: LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]; 42 | if (!available || error) { 43 | NSLog(@"TouchID is not available for device. Error: %@", error); 44 | } 45 | return available; 46 | } else { //iOS <= 7.1 47 | NSLog(@"You need iOS 8 to use TouchID local authentication"); 48 | return NO; 49 | } 50 | #else 51 | NSLog(@"You need iOS 8 to use TouchID local authentication"); 52 | return NO; 53 | #endif 54 | } 55 | 56 | - (void)validateWithCompletion:(void (^)(BOOL, NSError *))completionBlock 57 | localizedReason:(NSString *)localizedReason { 58 | #if TARGET_IPHONE_SIMULATOR 59 | if (completionBlock) { 60 | completionBlock(YES, nil); 61 | } 62 | #else 63 | A0SimpleKeychain *keychain = [A0SimpleKeychain keychainWithService:@"TouchID"]; 64 | keychain.useAccessControl = YES; 65 | keychain.defaultAccessiblity = A0SimpleKeychainItemAccessibleWhenUnlockedThisDeviceOnly; 66 | NSString *message = localizedReason ?: NSLocalizedString(@"Please authenticate to continue...", @"Default reason"); 67 | [keychain deleteEntryForKey:kTouchIDEntryKey]; 68 | [keychain setString:[[NSBundle mainBundle] bundleIdentifier] forKey:kTouchIDEntryKey promptMessage:message]; 69 | BOOL success = [keychain stringForKey:kTouchIDEntryKey promptMessage:message] != nil; 70 | if (completionBlock) { 71 | completionBlock(success, nil); 72 | } 73 | #endif 74 | } 75 | 76 | - (void)validateWithCompletion:(void (^)(BOOL, NSError *))completionBlock { 77 | [self validateWithCompletion:completionBlock 78 | localizedReason:nil]; 79 | } 80 | @end 81 | -------------------------------------------------------------------------------- /TouchIDAuth/A0TouchIDAuthentication.h: -------------------------------------------------------------------------------- 1 | // A0TouchIDAuth.h 2 | // 3 | // Copyright (c) 2015 Auth0 (http://auth0.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import 24 | 25 | NS_ASSUME_NONNULL_BEGIN 26 | 27 | typedef void(^A0RegisterCompletionBlock)(); 28 | typedef void(^A0ErrorBlock)(NSError *error); 29 | 30 | typedef NS_ENUM(NSInteger, A0TouchIDAuthenticationError) { 31 | A0TouchIDAuthenticationErrorTouchIDNotAvailable = -1, 32 | A0TouchIDAuthenticationErrorTouchIDFailed, 33 | }; 34 | 35 | FOUNDATION_EXPORT NSString * const A0TouchIDAuthenticationErrorKey; 36 | 37 | /** 38 | `A0TouchIDAuth` performs a passwordless authentication using `JWT` and *Apple's TouchID*. 39 | 40 | It has the following steps: 41 | 42 | 1. RSA KeyPair generation. 43 | 2. Registration of Public Key. 44 | 3. Generate and Sign JWT in the device. 45 | 4. Authenticate user with generated JWT. 46 | 47 | The first two steps are only performed if no KeyPair is found in the iOS Keychain. And every time `A0TouchIDAuth` needs to access the KeyPair, it will validate if the user is the owner of the device using TouchID. The steps #2 and #4 must be implemented by developer using callbacks. 48 | 49 | There are three callbacks, `registerPublicKey`, `jwtPayload` and `authenticate`. 50 | 51 | The callback `registerPublicKey` will handle the registration of the public key against an API and must call `completionBlock` on success in order to continue with the flow (or `errorBlock` if it fails). 52 | 53 | The callback `jwtPayload` is called before generating the JWT in order to provide the JWT payload needed by your API endpoint. 54 | 55 | The callback `authenticate` will receive the signed JWT and will need to authenticate against your API endpoint. 56 | */ 57 | @interface A0TouchIDAuthentication : NSObject 58 | 59 | /** 60 | * Block to handle public key registration with an API Endpoint. It will receive 3 parameters: publicKey, completionBlock and errorBlock. The public key is formatted as a RSA public key. 61 | */ 62 | @property (copy, nonatomic) void(^registerPublicKey)(NSData *pubKey, A0RegisterCompletionBlock completionBlock, A0ErrorBlock errorBlock); 63 | 64 | /** 65 | * Block to return the paylod for the JWT to be signed by the device. It will be called each time a JWT needs to be generated and signed. By default `A0TouchIDAuth` will include `iat`, `exp` (30 sec) and `sub` (Public Key fingerprint) claims but you can override them or add more entries to the payload. 66 | */ 67 | @property (copy, nonatomic) NSDictionary *(^jwtPayload)(); 68 | 69 | /** 70 | * Block called with the signed JWT to authenticate against an API ednpoint. 71 | */ 72 | @property (copy, nonatomic) void(^authenticate)(NSString *jwt, A0ErrorBlock errorBlock); 73 | 74 | /** 75 | * Block called when an error occurred during the Authentication flow. 76 | */ 77 | @property (copy, nonatomic) void(^onError)(NSError *error); 78 | 79 | /** 80 | * Localized message displayed in TouchID prompt. 81 | */ 82 | @property (copy, nullable, nonatomic) NSString *localizedTouchIDMessage; 83 | 84 | /** 85 | * Starts the TouchID authentication flow. It will fail automatically if `isTouchIDAuthenticationAvailable` returns `NO`. 86 | */ 87 | - (void)start; 88 | 89 | /** 90 | * Checks whether TouchID is available. 91 | * 92 | * @return if TouchID is available or not. 93 | */ 94 | - (BOOL)isTouchIDAuthenticationAvailable; 95 | 96 | /** 97 | * Reset TouchID authentication info stored in the device. 98 | */ 99 | - (void)reset; 100 | 101 | @end 102 | 103 | NS_ASSUME_NONNULL_END -------------------------------------------------------------------------------- /TouchIDAuth/A0TouchIDAuthentication.m: -------------------------------------------------------------------------------- 1 | // A0TouchIDAuthentication.m 2 | // 3 | // Copyright (c) 2015 Auth0 (http://auth0.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import "A0TouchIDAuthentication.h" 24 | #import "NSData+A0JWTSafeBase64.h" 25 | #import "A0RSAKeyExporter.h" 26 | #import "A0JWTBuilder.h" 27 | #import "A0TouchID.h" 28 | 29 | #import 30 | #import 31 | 32 | #define kJWTTimeToLive 30 33 | 34 | NSString * const A0TouchIDAuthenticationErrorKey = @"A0TouchIDAuthenticationErrorKey"; 35 | 36 | @interface A0TouchIDAuthentication () 37 | @property (strong, nonatomic) A0SimpleKeychain *keychain; 38 | @property (strong, nonatomic) A0TouchID *touchID; 39 | @property (strong, nonatomic) A0RSAKeyExporter *exporter; 40 | @end 41 | 42 | @implementation A0TouchIDAuthentication 43 | 44 | - (instancetype)init { 45 | if (self) { 46 | _keychain = [A0SimpleKeychain keychainWithService:@"TouchIDAuthentication"]; 47 | _touchID = [[A0TouchID alloc] init]; 48 | _exporter = [[A0RSAKeyExporter alloc] init]; 49 | } 50 | return self; 51 | } 52 | 53 | - (void)start { 54 | NSAssert(self.registerPublicKey != nil && self.authenticate, @"register pubkey and authenticate blocks must be non-nil"); 55 | if ([self isTouchIDAuthenticationAvailable]) { 56 | [self performTouchIDChallenge]; 57 | } else { 58 | [self safeFailWithError:[A0TouchIDAuthentication touchIDNotAvailableError]]; 59 | } 60 | } 61 | 62 | - (BOOL)isTouchIDAuthenticationAvailable { 63 | return self.touchID.isAvailable; 64 | } 65 | 66 | - (void)reset { 67 | [self.keychain deleteRSAKeyWithTag:[self publicKeyTag]]; 68 | [self.keychain deleteRSAKeyWithTag:[self privateKeyTag]]; 69 | } 70 | 71 | #pragma mark - TouchID step 72 | 73 | - (void)performTouchIDChallenge { 74 | __weak A0TouchIDAuthentication *weakSelf = self; 75 | [self.touchID validateWithCompletion:^(BOOL success, NSError *error) { 76 | if (success) { 77 | [weakSelf checkKeyPair]; 78 | } else { 79 | [weakSelf safeFailWithError:[A0TouchIDAuthentication touchIDFailedWithError:error]]; 80 | } 81 | } localizedReason:self.localizedTouchIDMessage]; 82 | } 83 | 84 | #pragma mark - Key Pair generation step 85 | 86 | - (void)checkKeyPair { 87 | __weak A0TouchIDAuthentication *weakSelf = self; 88 | A0RegisterCompletionBlock completionBlock = ^{ 89 | [weakSelf generateJWT]; 90 | }; 91 | A0ErrorBlock errorBlock = ^(NSError *error) { 92 | [weakSelf safeFailWithError:error]; 93 | }; 94 | 95 | NSString *publicTag = [self publicKeyTag]; 96 | NSString *privateTag = [self privateKeyTag]; 97 | 98 | if ([self.keychain hasRSAKeyWithTag:publicTag]) { 99 | completionBlock(); 100 | } else { 101 | [self.keychain generateRSAKeyPairWithLength:A0SimpleKeychainRSAKeySize1024Bits 102 | publicKeyTag:publicTag 103 | privateKeyTag:privateTag]; 104 | NSData *publicKeyData = [self.exporter exportPublicKey:[self.keychain dataForRSAKeyWithTag:publicTag]]; 105 | if (self.registerPublicKey) { 106 | self.registerPublicKey(publicKeyData, completionBlock, errorBlock); 107 | } 108 | } 109 | } 110 | 111 | - (NSString *)publicKeyTag { 112 | return [[[NSBundle mainBundle] bundleIdentifier] stringByAppendingString:@".pubkey"]; 113 | } 114 | 115 | - (NSString *)privateKeyTag { 116 | return [[[NSBundle mainBundle] bundleIdentifier] stringByAppendingString:@".key"]; 117 | } 118 | 119 | #pragma mark - JWT step 120 | 121 | - (void)generateJWT { 122 | __weak A0TouchIDAuthentication *weakSelf = self; 123 | A0ErrorBlock errorBlock = ^(NSError *error) { 124 | [weakSelf safeFailWithError:error]; 125 | }; 126 | 127 | NSString *publicTag = [self publicKeyTag]; 128 | long expiration = [[NSDate dateWithTimeIntervalSinceNow:kJWTTimeToLive] timeIntervalSince1970]; 129 | long issuedAt = [[NSDate date] timeIntervalSince1970]; 130 | NSMutableDictionary *claims = [@{ 131 | @"sub": [self.exporter fingerprintOfKey:[self.keychain dataForRSAKeyWithTag:publicTag]], 132 | @"device": [[UIDevice currentDevice] name], 133 | @"exp": @(expiration), 134 | @"iat": @(issuedAt), 135 | } mutableCopy]; 136 | if (self.jwtPayload) { 137 | [claims addEntriesFromDictionary:self.jwtPayload()]; 138 | } 139 | 140 | A0JWTBuilder *builder = [[A0JWTBuilder alloc] init]; 141 | SecKeyRef keyRef = [self.keychain keyRefOfRSAKeyWithTag:[self privateKeyTag]]; 142 | NSString *jwt = [[[builder setJWTPayload:claims] signWithMethod:A0JWTSignMethodRS256 andKeyOrSecret:(__bridge id)(keyRef)] jwt]; 143 | 144 | if (self.authenticate) { 145 | self.authenticate(jwt, errorBlock); 146 | } 147 | } 148 | 149 | #pragma mark - Error methods 150 | 151 | + (NSError *)touchIDNotAvailableError { 152 | NSError *error = [[NSError alloc] initWithDomain:@"com.auth0.TouchIDAuthentication" 153 | code:A0TouchIDAuthenticationErrorTouchIDNotAvailable 154 | userInfo:@{ 155 | NSLocalizedDescriptionKey: NSLocalizedString(@"TouchID is not configured or supported in the device", @"TouchID not available"), 156 | }]; 157 | return error; 158 | } 159 | 160 | + (NSError *)touchIDFailedWithError:(NSError *)failError { 161 | NSMutableDictionary *userInfo = [@{ 162 | NSLocalizedDescriptionKey: NSLocalizedString(@"Failed to authenticate using TouchID", @"User failed to authenticate"), 163 | NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"TouchID verification failed. Please try again.", @"TouchID verification failed"), 164 | } mutableCopy]; 165 | if (failError) { 166 | userInfo[A0TouchIDAuthenticationErrorKey] = failError; 167 | 168 | } 169 | NSError *error = [[NSError alloc] initWithDomain:@"com.auth0.TouchIDAuthentication" 170 | code:A0TouchIDAuthenticationErrorTouchIDFailed 171 | userInfo:userInfo]; 172 | return error; 173 | } 174 | 175 | - (void)safeFailWithError:(NSError *)error { 176 | if (self.onError) { 177 | self.onError(error); 178 | } 179 | } 180 | @end 181 | -------------------------------------------------------------------------------- /TouchIDAuth/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 0.2.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /TouchIDAuth/NSData+A0JWTSafeBase64.h: -------------------------------------------------------------------------------- 1 | // NSData+A0JWTSafeBase64.h 2 | // 3 | // Copyright (c) 2015 Auth0 (http://auth0.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import 24 | 25 | @interface NSData (A0JWTSafeBase64) 26 | 27 | /** 28 | * Encode NSData in Safe Base64 and removes padding. 29 | * 30 | * @return safe Base64 string without padding. 31 | */ 32 | - (nonnull NSString *)a0_jwtSafeBase64String; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /TouchIDAuth/NSData+A0JWTSafeBase64.m: -------------------------------------------------------------------------------- 1 | // NSData+A0JWTSafeBase64.m 2 | // 3 | // Copyright (c) 2014 Auth0 (http://auth0.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import "NSData+A0JWTSafeBase64.h" 24 | 25 | @implementation NSData (A0JWTSafeBase64) 26 | 27 | - (NSString *)a0_jwtSafeBase64String { 28 | NSString *base64String = [self base64EncodedStringWithOptions:0]; 29 | base64String = [base64String stringByReplacingOccurrencesOfString:@"=" 30 | withString:@""]; 31 | 32 | base64String = [base64String stringByReplacingOccurrencesOfString:@"/" 33 | withString:@"_"]; 34 | 35 | base64String = [base64String stringByReplacingOccurrencesOfString:@"+" 36 | withString:@"-"]; 37 | return base64String; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /TouchIDAuth/TouchIDAuth.h: -------------------------------------------------------------------------------- 1 | // 2 | // TouchIDAuth.h 3 | // TouchIDAuth 4 | // 5 | // Created by Hernan Zalazar on 10/29/15. 6 | // Copyright © 2015 Auth0. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for TouchIDAuth. 12 | FOUNDATION_EXPORT double TouchIDAuthVersionNumber; 13 | 14 | //! Project version string for TouchIDAuth. 15 | FOUNDATION_EXPORT const unsigned char TouchIDAuthVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | #import 20 | -------------------------------------------------------------------------------- /TouchIDAuthServer/app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var uuid = require('node-uuid'); 3 | var bodyParser = require('body-parser'); 4 | var jwt = require('jsonwebtoken'); 5 | 6 | var app = express(); 7 | 8 | app.use(bodyParser.json()); 9 | 10 | app.get('/', function (req, res) { 11 | res.send('TouchID authentication server example'); 12 | }); 13 | 14 | app.post('/pubkey', function (req, res) { 15 | console.log("Registering a public key for user: " + req.body["user"]); 16 | var buf = new Buffer(req.body["key"], 'base64'); 17 | console.log(buf.toString()); 18 | res.status(200).send({"user": {"id": uuid.v4()}}); 19 | }); 20 | 21 | app.post('/login', function (req, res) { 22 | var token = req.body["jwt"]; 23 | console.log("Login with JWT: " + token); 24 | var payload = jwt.decode(token); 25 | console.log("Received payload " + JSON.stringify(payload)); 26 | res.status(200).end(); 27 | }); 28 | 29 | var server = app.listen(3000, function () { 30 | 31 | var host = server.address().address; 32 | var port = server.address().port; 33 | 34 | console.log('Example app listening at http://%s:%s', host, port); 35 | 36 | }); -------------------------------------------------------------------------------- /TouchIDAuthServer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TouchIDAuthServer", 3 | "version": "0.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Auth0", 10 | "license": "MIT", 11 | "dependencies": { 12 | "body-parser": "^1.9.2", 13 | "express": "^4.10.1", 14 | "jsonwebtoken": "^1.1.2", 15 | "node-uuid": "^1.4.1" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /TouchIDAuthTests/A0JWTSafeBase64Spec.swift: -------------------------------------------------------------------------------- 1 | // A0JWTSafeBase64Spec.swift 2 | // 3 | // Copyright (c) 2015 Auth0 (http://auth0.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | import Quick 24 | import Nimble 25 | import Security 26 | 27 | func randomDataOfSize(size: Int) -> NSData { 28 | let data = NSMutableData(length: size)! 29 | SecRandomCopyBytes(kSecRandomDefault, size, UnsafeMutablePointer(data.mutableBytes)) 30 | return data 31 | } 32 | 33 | class A0JWTSafeBase64Spec: QuickSpec { 34 | override func spec() { 35 | 36 | var base64: String! 37 | 38 | beforeEach { 39 | let size = Int(arc4random_uniform(400) + 200) 40 | base64 = randomDataOfSize(size).a0_jwtSafeBase64String() 41 | } 42 | 43 | it("should not include invalid characters") { 44 | let urlSafeBase64Set = NSCharacterSet.alphanumericCharacterSet().mutableCopy() 45 | urlSafeBase64Set.formIntersectionWithCharacterSet(NSCharacterSet.nonBaseCharacterSet().invertedSet) 46 | urlSafeBase64Set.addCharactersInString("-_") 47 | let set = urlSafeBase64Set.invertedSet 48 | expect(base64.rangeOfCharacterFromSet(set)).to(beNil()) 49 | } 50 | 51 | it("should not include '='") { 52 | expect(base64).notTo(contain("=")) 53 | } 54 | 55 | it("should not include '/'") { 56 | expect(base64).notTo(contain("/")) 57 | } 58 | 59 | it("should not include '+'") { 60 | expect(base64).notTo(contain("+")) 61 | } 62 | 63 | } 64 | } -------------------------------------------------------------------------------- /TouchIDAuthTests/A0RSAKeyExporterSpec.swift: -------------------------------------------------------------------------------- 1 | // A0RSAKeyExporterSpec.swift 2 | // 3 | // Copyright (c) 2015 Auth0 (http://auth0.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | import Quick 24 | import Nimble 25 | 26 | class A0RSAKeyExporterSpec: QuickSpec { 27 | override func spec() { 28 | 29 | var exporter: A0RSAKeyExporter! 30 | 31 | beforeEach { 32 | exporter = A0RSAKeyExporter() 33 | } 34 | 35 | describe("export public key") { 36 | var value: String! 37 | 38 | beforeEach { 39 | let data = NSUUID().UUIDString.dataUsingEncoding(NSUTF8StringEncoding)! 40 | value = String(data: exporter.exportPublicKey(data)!, encoding: NSUTF8StringEncoding) 41 | } 42 | 43 | it("should contain header") { 44 | expect(value).to(beginWith("-----BEGIN RSA PUBLIC KEY-----")) 45 | } 46 | 47 | it("should contain footer") { 48 | expect(value).to(endWith("-----END RSA PUBLIC KEY-----")) 49 | } 50 | 51 | it("should have 3 parts") { 52 | expect(value.componentsSeparatedByString("\n")).to(haveCount(3)) 53 | } 54 | 55 | it("should have key in base64 with max length of 64") { 56 | let base64Key = value.componentsSeparatedByString("\n").first! 57 | expect(1...64 ~= base64Key.characters.count).to(beTrue()) 58 | } 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /TouchIDAuthTests/A0TouchIDAuthenticationSpec.swift: -------------------------------------------------------------------------------- 1 | // A0TouchIDAuthenticationSpec.swift 2 | // 3 | // Copyright (c) 2014 Auth0 (http://auth0.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | import Quick 24 | import Nimble 25 | import JWTDecode 26 | 27 | class MockTouchID: A0TouchID { 28 | var availavility: Bool = true 29 | var onValidation: () -> (valid: Bool, error: NSError?) = { return (true, nil) } 30 | 31 | override var available: Bool { 32 | return self.availavility 33 | } 34 | 35 | override func validateWithCompletion(completionBlock: ((Bool, NSError!) -> Void)!, localizedReason reason: String!) { 36 | let result = onValidation() 37 | completionBlock(result.valid, result.error) 38 | } 39 | } 40 | 41 | class A0TouchIDAuthenticationSpec: QuickSpec { 42 | 43 | override func spec() { 44 | var authentication: A0TouchIDAuthentication! 45 | var touchID: MockTouchID! 46 | 47 | beforeEach { 48 | touchID = MockTouchID() 49 | authentication = A0TouchIDAuthentication() 50 | authentication.touchID = touchID 51 | authentication.registerPublicKey = { (_, completion, _) in completion() } 52 | authentication.authenticate = { (jwt, error) in } 53 | } 54 | 55 | afterEach { 56 | authentication.reset() 57 | } 58 | 59 | describe("authentication") { 60 | 61 | it("should register public key") { 62 | authentication.reset() 63 | waitUntil { done in 64 | authentication.registerPublicKey = { (_, _, _) in 65 | done() 66 | } 67 | authentication.start() 68 | } 69 | } 70 | 71 | it("should only register public key once") { 72 | authentication.start() 73 | authentication.registerPublicKey = { (_, _, _) in fail("public key already registered") } 74 | authentication.start() 75 | } 76 | 77 | it("should not start without callbacks") { 78 | authentication = A0TouchIDAuthentication() 79 | authentication.touchID = touchID 80 | expect { 81 | authentication.start() 82 | }.to(raiseException()) 83 | } 84 | 85 | it("should fail with when TouchID is not available") { 86 | touchID.availavility = false 87 | waitUntil { done in 88 | authentication.onError = { error in 89 | expect(error.code).to(equal(A0TouchIDAuthenticationError.TouchIDNotAvailable.rawValue)) 90 | done() 91 | } 92 | authentication.start() 93 | } 94 | } 95 | 96 | it("should call error callback on TouchID validation failure") { 97 | touchID.onValidation = { return (false, nil) } 98 | waitUntil { done in 99 | authentication.onError = { error in 100 | expect(error.code).to(equal(A0TouchIDAuthenticationError.TouchIDFailed.rawValue)) 101 | done() 102 | } 103 | authentication.start() 104 | } 105 | } 106 | 107 | describe("jwt payload") { 108 | it("should include default values") { 109 | waitUntil { done in 110 | authentication.authenticate = { (jwtString, _) in 111 | let jwt = try! decode(jwtString) 112 | expect(jwt.expiresAt).notTo(beNil()) 113 | expect(jwt.issuedAt).notTo(beNil()) 114 | expect(jwt.subject).notTo(beNil()) 115 | expect(jwt.claim("device") as String?).to(equal(UIDevice.currentDevice().name)) 116 | done() 117 | } 118 | authentication.start() 119 | } 120 | } 121 | 122 | it("should expire 30 seconds after issued") { 123 | waitUntil { done in 124 | authentication.authenticate = { (jwtString, _) in 125 | let jwt = try! decode(jwtString) 126 | let expiresIn = jwt.expiresAt!.timeIntervalSinceDate(jwt.issuedAt!) 127 | expect(expiresIn).to(equal(30)) 128 | done() 129 | } 130 | authentication.start() 131 | } 132 | } 133 | 134 | it("should also include custom values") { 135 | waitUntil { done in 136 | authentication.jwtPayload = { return ["custom": "custom_value"] } 137 | authentication.authenticate = { (jwtString, _) in 138 | let jwt = try! decode(jwtString) 139 | expect(jwt.expiresAt).notTo(beNil()) 140 | expect(jwt.issuedAt).notTo(beNil()) 141 | expect(jwt.subject).notTo(beNil()) 142 | expect(jwt.claim("custom") as String?).notTo(beNil()) 143 | done() 144 | } 145 | authentication.start() 146 | } 147 | } 148 | 149 | } 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /TouchIDAuthTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 0.2.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /TouchIDAuthTests/TouchIDAuthTests-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #import 6 | #import "A0RSAKeyExporter.h" 7 | #import "NSData+A0JWTSafeBase64.h" 8 | #import "A0TouchID.h" 9 | 10 | @interface A0TouchIDAuthentication (Testing) 11 | 12 | @property (strong, nonatomic) A0TouchID *touchID; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /script/.env: -------------------------------------------------------------------------------- 1 | 2 | REQUIRED_CARTHAGE_VERSION=0.9.3 3 | CARTHAGE_BUILD_PLATFORM=${CARTHAGE_BUILD_PLATFORM:-"iOS"} 4 | CARTHAGE_NO_USE_BINARIES=${CARTHAGE_NO_USE_BINARIES:-"true"} 5 | PROJECT_NAME=TouchIDAuth 6 | XCODE_WORKSPACE= 7 | XCODE_PROJECT=TouchIDAuth.xcodeproj 8 | IOS_DESTINATION_VERSION=${IOS_DESTINATION_VERSION:-"latest"} 9 | IOS_DESTINATION_SIMULATOR_NAME=${IOS_DESTINATION_SIMULATOR_NAME:-"iPhone 6"} 10 | OSX_DESTINATION_ARCH=${OSX_DESTINATION_ARCH:-""} 11 | -------------------------------------------------------------------------------- /script/bootstrap: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | install_homebrew () 6 | { 7 | if [ -z $GITHUB_ACCESS_TOKEN ] 8 | then 9 | export HOMEBREW_GITHUB_API_TOKEN=$GITHUB_ACCESS_TOKEN 10 | fi 11 | 12 | if type brew > /dev/null 13 | then 14 | echo " ✔ brew is already installed" 15 | 16 | if [ -z "$SKIP_BREW_FORMULAS_UPDATE" ] 17 | then 18 | echo "" 19 | echo " → Updating homebrew formulas" 20 | brew update > /dev/null || brew update > /dev/null 21 | echo " ✔ formulas updated" 22 | fi 23 | else 24 | command -v ruby >/dev/null 2>&1 || { echo >&2 "Error: Some ruby of version is required to install homebrew. Aborting"; exit 1; } 25 | ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 26 | fi 27 | } 28 | 29 | # param $1 formula name 30 | # param $2 [optional] tap path 31 | brew_install () 32 | { 33 | formula_version=`brew list --versions $1` 34 | if [ -z "$formula_version" ] 35 | then 36 | if [ -z $2 ] 37 | then 38 | formula_name=$1 39 | else 40 | formula_name="$2/$1" 41 | fi 42 | echo "" 43 | echo " → Installing brew formula $formula_name" 44 | brew install -v $formula_name > /dev/null 2>&1 45 | 46 | # Extract version 47 | regexp="^.*([0-9]\.[0-9]\.[0-9]).*$" 48 | installed_version="" 49 | eval "output=\"$(brew info $1)\"" 50 | if [[ $output =~ $regexp ]] 51 | then 52 | installed_version=${BASH_REMATCH[1]} 53 | fi 54 | 55 | echo " ✔ $formula_name $installed_version has been installed" 56 | else 57 | echo " ✔ $1 is already installed" 58 | fi 59 | } 60 | 61 | print_gem_install_cmd () 62 | { 63 | regexp="gem ['\"]([a-zA-Z0-9_-]+)['\"](,.*)?" 64 | gems="" 65 | while read -r line 66 | do 67 | if [[ $line =~ $regexp ]] 68 | then 69 | gems="$gems ${BASH_REMATCH[1]}" 70 | fi 71 | done < Gemfile 72 | 73 | echo "" 74 | echo " $> 'sudo gem install$gems'" 75 | echo "" 76 | } 77 | 78 | bundle_install () 79 | { 80 | echo "" 81 | echo " → Installing gems" 82 | echo "" 83 | if type bundle > /dev/null 84 | then 85 | bundle install 86 | else 87 | # TODO ask user if he/she wants the script to try to install 88 | # rbenv, ruby and bundler. 89 | printf "\033[1;33m⚠ WARNING: Ruby gems in Gemfile could not be installed because 'bundler' is not available.\n" \ 90 | "You should install rbenv or rvm and bundler" \ 91 | "or try to install the gems globally by running the following command:" 92 | print_gem_install_cmd 93 | printf "\033[0m" 94 | exit 1 95 | fi 96 | } 97 | 98 | install_git_hooks () 99 | { 100 | if [ ! -z "$INSTALL_GITHOOKS" ] 101 | then 102 | echo "" 103 | echo " → Installing git hooks" 104 | echo "" 105 | for hook in script/git_hooks/* 106 | do 107 | cp $hook .git/hooks 108 | echo " ✔ $hook successfully installed" 109 | done 110 | echo "" 111 | fi 112 | } 113 | 114 | bootstrap_carthage () 115 | { 116 | echo "" 117 | echo " → Bootstrapping Carthage" 118 | echo "" 119 | carthage_cmd="time carthage bootstrap --platform $CARTHAGE_BUILD_PLATFORM" 120 | 121 | if [ "$USE_SSH" == "true" ] 122 | then 123 | carthage_cmd="$carthage_cmd --use-ssh" 124 | fi 125 | if [ "$USE_SUBMODULES" == "true" ] 126 | then 127 | carthage_cmd="$carthage_cmd --use-submodules --no-build" 128 | fi 129 | 130 | if [ "$CARTHAGE_NO_USE_BINARIES" == "true" ] 131 | then 132 | carthage_cmd="$carthage_cmd --no-use-binaries" 133 | fi 134 | 135 | eval $carthage_cmd 136 | } 137 | 138 | bootstrap_cocoapods () 139 | { 140 | echo "" 141 | echo " → Bootstrapping Cocoapods" 142 | echo "" 143 | if type bundle > /dev/null && bundle show pod > /dev/null 144 | then 145 | bundle exec pod install 146 | else 147 | pod install 148 | fi 149 | } 150 | 151 | echo_submodule_name () 152 | { 153 | echo " ✔ $name successfully initialized" 154 | } 155 | 156 | init_submodules () 157 | { 158 | echo "" 159 | echo " → Initializing submodules ..." 160 | echo "" 161 | git submodule update --quiet --init --recursive > /dev/null 162 | git submodule foreach --quiet echo_submodule_name 163 | } 164 | 165 | install_dependencies () 166 | { 167 | echo "" 168 | echo " → Installing dependencies" 169 | echo "" 170 | install_homebrew 171 | brew_install "xcode-coveralls" "macmade/tap" 172 | 173 | if [ -f script/script_hooks/bootstrap ] 174 | then 175 | script/script_hooks/bootstrap 176 | fi 177 | } 178 | 179 | install_carthage () 180 | { 181 | source script/common/carthage 182 | 183 | if type carthage > /dev/null 184 | then 185 | echo "" 186 | echo " → Checking installed version of carthage" 187 | echo "" 188 | check_carthage_version 189 | else 190 | force_install_carthage 191 | fi 192 | } 193 | 194 | main () 195 | { 196 | source script/.env 197 | 198 | echo "" 199 | echo " Bootstrapping $PROJECT_NAME" 200 | echo "" 201 | 202 | install_git_hooks 203 | install_dependencies 204 | 205 | if [ -f Cartfile ] 206 | then 207 | install_carthage 208 | bootstrap_carthage 209 | fi 210 | 211 | if [ -f Gemfile ] 212 | then 213 | bundle_install 214 | fi 215 | 216 | if [ -f Podfile ] 217 | then 218 | bootstrap_cocoapods 219 | fi 220 | 221 | if [ -f .gitmodules ] 222 | then 223 | init_submodules 224 | fi 225 | 226 | open_file_name="" 227 | if [ -z "$XCODE_WORKSPACE" ] 228 | then 229 | open_file_name=$XCODE_PROJECT 230 | else 231 | open_file_name=$XCODE_WORKSPACE 232 | fi 233 | 234 | echo "" 235 | echo " $PROJECT_NAME successfully bootstrapped" 236 | echo "" 237 | echo " Usefull scripts:" 238 | echo "" 239 | echo " * 'script/test' to run tests." 240 | echo " * 'script/build' to build the project." 241 | echo " * 'script/update' to update project's dependencies." 242 | echo "" 243 | echo " You can start hacking by executing:" 244 | echo "" 245 | echo " open $open_file_name" 246 | echo "" 247 | } 248 | 249 | export -f init_submodules 250 | export -f echo_submodule_name 251 | export -f brew_install 252 | 253 | main 254 | -------------------------------------------------------------------------------- /script/build: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | source script/common/carthage 6 | 7 | build_using_carthage () 8 | { 9 | check_carthage_version 10 | carthage_cmd="carthage build --no-skip-current --platform $CARTHAGE_BUILD_PLATFORM" 11 | if [ "$USE_SSH" == "true" ] 12 | then 13 | carthage_cmd="$carthage_cmd --use-ssh" 14 | fi 15 | if [ ! -z "$VERBOSE" ] 16 | then 17 | echo $carthage_cmd 18 | fi 19 | eval $carthage_cmd 20 | } 21 | 22 | build_using_xcodebuild () 23 | { 24 | build_command="set -o pipefail && xcodebuild -scheme $1" 25 | if [ -z "$XCODE_WORKSPACE" ] 26 | then 27 | build_command="$build_command -project $XCODE_PROJECT" 28 | else 29 | build_command="$build_command -workspace $XCODE_WORKSPACE" 30 | fi 31 | build_command="$build_command -sdk iphonesimulator build -configuration Debug" 32 | 33 | if type bundle > /dev/null && bundle show xcpretty > /dev/null 34 | then 35 | build_command="$build_command | xcpretty -c" 36 | fi 37 | 38 | echo "" 39 | echo " → Building scheme '$1'" 40 | echo "" 41 | if [ ! -z "$VERBOSE" ] 42 | then 43 | echo $build_command 44 | fi 45 | eval $build_command 46 | } 47 | 48 | if [ ! -f $XCODE_WORKSPACE ] && [ -f Cartfile ] && type carthage > /dev/null 49 | then 50 | build_using_carthage 51 | else 52 | source script/.env 53 | source script/script_hooks/schemes 54 | 55 | current_schemes=$(schemes) 56 | if [ -z "$current_schemes" ] 57 | then 58 | echo "" 59 | echo "ERROR: There are no schemes. Probably you forgot to share your schemes" 60 | exit 1 61 | fi 62 | 63 | for scheme in $current_schemes 64 | do 65 | build_using_xcodebuild $scheme 66 | done 67 | fi 68 | -------------------------------------------------------------------------------- /script/certificates/cibot.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auth0/TouchIDAuth/78f5130a66e58d7ee533ed9f75fe991f84460ad3/script/certificates/cibot.p12 -------------------------------------------------------------------------------- /script/cibuild: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export SCRIPT_DIR=$(dirname "$0") 4 | 5 | ## 6 | ## Configuration Variables 7 | ## 8 | 9 | # The name of the keychain to create for iOS code signing. 10 | KEYCHAIN=ios-build.keychain 11 | 12 | ## 13 | ## Build Process 14 | ## 15 | 16 | main () 17 | { 18 | if [ -f Cartfile ] && [ -f "$SCRIPT_DIR/certificates/cibot.p12" ] 19 | then 20 | echo "" 21 | echo "####### Importing Developer Certificates #######" 22 | echo "" 23 | import_certs 24 | fi 25 | 26 | echo "" 27 | echo "####### Bootstrap Phase #######" 28 | echo "" 29 | NO_CARTHAGE_UPDATE=true script/bootstrap 30 | local status=$? 31 | 32 | if [ $status -eq 0 ] 33 | then 34 | echo "" 35 | echo "" 36 | echo "####### Build & Test Phase #######" 37 | echo "" 38 | set -o pipefail && script/test 2>&1 | tee /tmp/build.test-output.txt 39 | status=$? 40 | if [ ! $status -eq 0 ] 41 | then 42 | log_file_path=`cat /tmp/build.test-output.txt | tail -n 100 | perl -l -ne '/(\/var\/folders.*\/com\.apple\.dt\.XCTest-status.*)\)/ && print $1'` 43 | if [ ! -z "$log_file_path" ] 44 | then 45 | echo "" 46 | echo " → The tests have failed. Printing output of log file '$log_file_path'." 47 | cat $log_file_path 48 | echo "" 49 | fi 50 | fi 51 | fi 52 | 53 | if [ -f Cartfile ] && [ -f "$SCRIPT_DIR/certificates/cibot.p12" ] 54 | then 55 | delete_keychain 56 | fi 57 | exit $status 58 | } 59 | 60 | import_certs () 61 | { 62 | # If this environment variable is missing, we must not be running on Travis. 63 | if [ -z "$KEY_PASSWORD" ] 64 | then 65 | return 0 66 | fi 67 | 68 | echo " → Setting up code signing..." 69 | local password=cibuild 70 | 71 | # Create a temporary keychain for code signing. 72 | security create-keychain -p "$password" "$KEYCHAIN" 73 | security default-keychain -s "$KEYCHAIN" 74 | security unlock-keychain -p "$password" "$KEYCHAIN" 75 | security set-keychain-settings -t 3600 -l "$KEYCHAIN" 76 | 77 | # Download the certificate for the Apple Worldwide Developer Relations 78 | # Certificate Authority. 79 | local certpath="$SCRIPT_DIR/apple_wwdr.cer" 80 | curl -s 'https://developer.apple.com/certificationauthority/AppleWWDRCA.cer' > "$certpath" 81 | security import "$certpath" -k "$KEYCHAIN" -T /usr/bin/codesign 82 | 83 | # Import our development certificate. 84 | security import "$SCRIPT_DIR/certificates/cibot.p12" -k "$KEYCHAIN" -P "$KEY_PASSWORD" -T /usr/bin/codesign 85 | } 86 | 87 | delete_keychain () 88 | { 89 | if [ -z "$KEY_PASSWORD" ] 90 | then 91 | return 0 92 | fi 93 | 94 | echo " → Removing temporary keychain" 95 | security delete-keychain "$KEYCHAIN" 96 | echo " ✔ Temporary keychain successfully removed." 97 | } 98 | 99 | export -f import_certs 100 | export -f delete_keychain 101 | 102 | main 103 | -------------------------------------------------------------------------------- /script/common/carthage: -------------------------------------------------------------------------------- 1 | force_install_carthage () 2 | { 3 | echo "" 4 | echo " → Installing carthage '$REQUIRED_CARTHAGE_VERSION'" 5 | echo "" 6 | curl -s -L -O https://github.com/Carthage/Carthage/releases/download/$REQUIRED_CARTHAGE_VERSION/Carthage.pkg > /dev/null 7 | sudo installer -pkg Carthage.pkg -target / > /dev/null 8 | rm Carthage.pkg 9 | echo " ✔ carthage '$REQUIRED_CARTHAGE_VERSION' successfully installed" 10 | echo "" 11 | } 12 | 13 | uninstall_carthage () 14 | { 15 | echo "" 16 | echo " → Uninstalling carthage" 17 | echo "" 18 | local carthage_installed_version=`carthage version` 19 | if type brew > /dev/null && [ ! -z "$(brew list --versions carthage)" ] 20 | then 21 | brew uninstall carthage > /dev/null 22 | else 23 | sudo rm -frd /Library/Frameworks/CarthageKit.framework 24 | sudo rm /usr/local/bin/carthage 25 | fi 26 | echo " ✔ carthage '$carthage_installed_version' successfully uninstalled" 27 | } 28 | 29 | check_carthage_version () 30 | { 31 | local carthage_installed_version=`carthage version` 32 | if [ "$carthage_installed_version" != "$REQUIRED_CARTHAGE_VERSION" ] 33 | then 34 | printf "\033[1;31mError: carthage version '$carthage_installed_version' is not equal to '$REQUIRED_CARTHAGE_VERSION'" 35 | printf "\033[0m" 36 | if [ ! -z "$NO_CARTHAGE_UPDATE" ] 37 | then 38 | exit 1 39 | else 40 | echo "" 41 | echo "" 42 | echo "Would you like to update carthage to version '$REQUIRED_CARTHAGE_VERSION'? [N/y]" 43 | read update_carthage 44 | if [ "$update_carthage" == "y" ] 45 | then 46 | uninstall_carthage 47 | force_install_carthage 48 | else 49 | exit 1 50 | fi 51 | fi 52 | fi 53 | } 54 | -------------------------------------------------------------------------------- /script/coverage: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | source script/.env 6 | 7 | if type xcode-coveralls > /dev/null 8 | then 9 | if [ ! -f script/xcenv.sh ] 10 | then 11 | # Running the test generates the xcenv.sh 12 | script/test 13 | fi 14 | 15 | if [ -f script/xcenv.sh ] 16 | then 17 | source script/xcenv.sh 18 | declare -r DIR_BUILD="${OBJECT_FILE_DIR_normal}/${CURRENT_ARCH}/" 19 | xcode-coveralls --include $SRCROOT --exclude "$SRCROOT""Tests" --exclude Carthage --exclude Pods --token $COVERALLS_TOKEN "${DIR_BUILD}" 20 | else 21 | # TODO print instruction of how to add the generation of xcenv.sh 22 | echo "" 23 | echo " Error: script/xcenv.sh was not generated after running 'script/test'." 24 | echo "" 25 | exit 1 26 | fi 27 | fi 28 | -------------------------------------------------------------------------------- /script/git_hooks/pre-push: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # An example hook script to verify what is about to be pushed. Called by "git 4 | # push" after it has checked the remote status, but before anything has been 5 | # pushed. If this script exits with a non-zero status nothing will be pushed. 6 | # 7 | # This hook is called with the following parameters: 8 | # 9 | # $1 -- Name of the remote to which the push is being done 10 | # $2 -- URL to which the push is being done 11 | # 12 | # If pushing without using a named remote those arguments will be equal. 13 | # 14 | # Information about the commits which are being pushed is supplied as lines to 15 | # the standard input in the form: 16 | # 17 | # 18 | # 19 | # This sample shows how to prevent push of commits where the log message starts 20 | # with "WIP" (work in progress). 21 | 22 | set -e 23 | 24 | remote="$1" 25 | url="$2" 26 | 27 | script/test 28 | -------------------------------------------------------------------------------- /script/script_hooks/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auth0/TouchIDAuth/78f5130a66e58d7ee533ed9f75fe991f84460ad3/script/script_hooks/.gitkeep -------------------------------------------------------------------------------- /script/script_hooks/destinations: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | IOS_DESTINATION="'platform=iOS Simulator,name=$IOS_DESTINATION_SIMULATOR_NAME,OS=$IOS_DESTINATION_VERSION'" 4 | 5 | if [ -z "$OSX_DESTINATION_ARCH" ] 6 | then 7 | OSX_DESTINATION="'platform=OS X'" 8 | else 9 | OSX_DESTINATION="'platform=OS X,arch=$OSX_DESTINATION_ARCH'" 10 | fi 11 | 12 | scheme_destination () 13 | { 14 | shopt -s nocasematch 15 | 16 | case "$1" in 17 | *iOS) 18 | destination=$IOS_DESTINATION 19 | ;; 20 | *OSX) 21 | destination=$OSX_DESTINATION 22 | ;; 23 | *) 24 | destination=$IOS_DESTINATION 25 | ;; 26 | esac 27 | 28 | echo $destination 29 | } -------------------------------------------------------------------------------- /script/script_hooks/schemes: -------------------------------------------------------------------------------- 1 | schemes () 2 | { 3 | if [ -z "$SCHEME" ] 4 | then 5 | xcodebuild -list | awk '{if(found) print} /Schemes/{found=1}' | awk '{$1=$1};1' 6 | else 7 | echo $SCHEME 8 | fi 9 | } 10 | -------------------------------------------------------------------------------- /script/test: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | source script/.env 6 | source script/script_hooks/schemes 7 | source script/script_hooks/destinations 8 | source script/common/carthage 9 | 10 | run_tests () 11 | { 12 | test_command="set -o pipefail && xcodebuild -scheme $1" 13 | 14 | if [ -z "$XCODE_WORKSPACE" ] 15 | then 16 | test_command="$test_command -project $XCODE_PROJECT" 17 | else 18 | test_command="$test_command -workspace $XCODE_WORKSPACE" 19 | fi 20 | 21 | destination=$(scheme_destination $1) 22 | test_command="$test_command -destination $destination clean build test -configuration Debug" 23 | 24 | if [ ! -z "$CIRCLE_ARTIFACTS" ] 25 | then 26 | test_command="$test_command | tee $CIRCLE_ARTIFACTS/xcode_raw.log" 27 | fi 28 | if type bundle > /dev/null && bundle show xcpretty > /dev/null 29 | then 30 | test_command="$test_command | bundle exec xcpretty -c" 31 | if [ ! -z "$CIRCLE_TEST_REPORTS" ] 32 | then 33 | test_command="$test_command --report junit --output $CIRCLE_TEST_REPORTS/xcode/results.xml" 34 | fi 35 | fi 36 | 37 | echo "" 38 | echo " → Running tests for scheme '$1'" 39 | echo "" 40 | if [ ! -z "$VERBOSE" ] 41 | then 42 | echo $test_command 43 | fi 44 | eval $test_command 45 | } 46 | 47 | if [ -f Cartfile ] 48 | then 49 | check_carthage_version 50 | fi 51 | 52 | current_schemes=$(schemes) 53 | if [ -z "$current_schemes" ] 54 | then 55 | echo "" 56 | echo "ERROR: There are no schemes. Probably you forgot to share your schemes" 57 | exit 1 58 | fi 59 | 60 | for scheme in $current_schemes 61 | do 62 | run_tests $scheme 63 | done 64 | 65 | if [ -f "$PROJECT_NAME.podspec" ] 66 | then 67 | echo "" 68 | echo " → Linting $PROJECT_NAME.podspec" 69 | echo "" 70 | if type bundle > /dev/null && bundle show pod > /dev/null 71 | then 72 | bundle exec pod lib lint 73 | elif type pod > /dev/null 74 | then 75 | pod lib lint 76 | fi 77 | fi 78 | -------------------------------------------------------------------------------- /script/update: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | source script/.env 6 | source script/common/carthage 7 | 8 | if [ -f Cartfile ] && type carthage > /dev/null 9 | then 10 | check_carthage_version 11 | carthage_cmd="carthage update --platform $CARTHAGE_BUILD_PLATFORM" 12 | if [ "$USE_SSH" == "true" ] 13 | then 14 | carthage_cmd="$carthage_cmd --use-ssh" 15 | fi 16 | if [ "$USE_SUBMODULES" == "true" ] 17 | then 18 | carthage_cmd="$carthage_cmd --use-submodules --no-build" 19 | fi 20 | eval $carthage_cmd 21 | elif [ -f Podfile ] 22 | then 23 | if type bundle > /dev/null && bundle show pod > /dev/null 24 | then 25 | bundle exec pod update 26 | else 27 | pod update 28 | fi 29 | fi 30 | --------------------------------------------------------------------------------