├── .gitignore ├── CognitoApplication.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── CognitoApplication.xcworkspace └── contents.xcworkspacedata ├── CognitoApplication ├── AppDelegate.swift ├── AppViewController.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── CognitoConfig.swift ├── ForgotPasswordViewController.swift ├── Info.plist ├── LoginViewController.swift ├── MultiFactorAuthenticationController.swift ├── ResetPasswordViewController.swift ├── SignupViewController.swift └── VerificationViewController.swift ├── LICENSE ├── Podfile └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | Podfile.lock 2 | Pods 3 | CognitoApplication.xcodeproj/project.xcworkspace/xcuserdata 4 | CognitoApplication.xcodeproj/xcuserdata 5 | CognitoApplication.xcworkspace/xcuserdata 6 | CognitoConfig.plist 7 | -------------------------------------------------------------------------------- /CognitoApplication.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 632D25211EB81732005A4E31 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 632D25201EB81732005A4E31 /* AppDelegate.swift */; }; 11 | 632D25261EB81732005A4E31 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 632D25241EB81732005A4E31 /* Main.storyboard */; }; 12 | 632D25281EB81732005A4E31 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 632D25271EB81732005A4E31 /* Assets.xcassets */; }; 13 | 632D252B1EB81732005A4E31 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 632D25291EB81732005A4E31 /* LaunchScreen.storyboard */; }; 14 | 632D25351EB95CBD005A4E31 /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 632D25341EB95CBD005A4E31 /* LoginViewController.swift */; }; 15 | 632D25371EB95CCE005A4E31 /* ResetPasswordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 632D25361EB95CCE005A4E31 /* ResetPasswordViewController.swift */; }; 16 | 632D25391EB95CDE005A4E31 /* AppViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 632D25381EB95CDE005A4E31 /* AppViewController.swift */; }; 17 | 6373365E1F3252B700EA5BCE /* ForgotPasswordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6373365D1F3252B700EA5BCE /* ForgotPasswordViewController.swift */; }; 18 | 6398A6701EC15413009C2D77 /* CognitoConfig.plist in Resources */ = {isa = PBXBuildFile; fileRef = 6398A66F1EC15413009C2D77 /* CognitoConfig.plist */; }; 19 | 6398A6721EC1548D009C2D77 /* CognitoConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6398A6711EC1548D009C2D77 /* CognitoConfig.swift */; }; 20 | 63A8F7D51F30E79B00AD21E0 /* SignupViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63A8F7D41F30E79B00AD21E0 /* SignupViewController.swift */; }; 21 | 63A8F7D71F30FAC800AD21E0 /* VerificationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63A8F7D61F30FAC800AD21E0 /* VerificationViewController.swift */; }; 22 | 63B5E4F31F33EBC300B9D1A3 /* MultiFactorAuthenticationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63B5E4F21F33EBC300B9D1A3 /* MultiFactorAuthenticationController.swift */; }; 23 | 6E3BB744AD21E1DD722F496E /* Pods_CognitoApplication.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50439609D2FD5FDF1A0EA3E5 /* Pods_CognitoApplication.framework */; }; 24 | /* End PBXBuildFile section */ 25 | 26 | /* Begin PBXFileReference section */ 27 | 50439609D2FD5FDF1A0EA3E5 /* Pods_CognitoApplication.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_CognitoApplication.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | 5D6FA8A57C007EC439FA41F7 /* Pods-CognitoApplication.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CognitoApplication.debug.xcconfig"; path = "Pods/Target Support Files/Pods-CognitoApplication/Pods-CognitoApplication.debug.xcconfig"; sourceTree = ""; }; 29 | 632D251D1EB81732005A4E31 /* CognitoApplication.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CognitoApplication.app; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | 632D25201EB81732005A4E31 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 31 | 632D25251EB81732005A4E31 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 32 | 632D25271EB81732005A4E31 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 33 | 632D252A1EB81732005A4E31 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 34 | 632D252C1EB81732005A4E31 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 35 | 632D25341EB95CBD005A4E31 /* LoginViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginViewController.swift; sourceTree = ""; }; 36 | 632D25361EB95CCE005A4E31 /* ResetPasswordViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResetPasswordViewController.swift; sourceTree = ""; }; 37 | 632D25381EB95CDE005A4E31 /* AppViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppViewController.swift; sourceTree = ""; }; 38 | 6373365D1F3252B700EA5BCE /* ForgotPasswordViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ForgotPasswordViewController.swift; sourceTree = ""; }; 39 | 6398A66F1EC15413009C2D77 /* CognitoConfig.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = CognitoConfig.plist; sourceTree = ""; }; 40 | 6398A6711EC1548D009C2D77 /* CognitoConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CognitoConfig.swift; sourceTree = ""; }; 41 | 63A8F7D41F30E79B00AD21E0 /* SignupViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignupViewController.swift; sourceTree = ""; }; 42 | 63A8F7D61F30FAC800AD21E0 /* VerificationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VerificationViewController.swift; sourceTree = ""; }; 43 | 63B5E4F21F33EBC300B9D1A3 /* MultiFactorAuthenticationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MultiFactorAuthenticationController.swift; sourceTree = ""; }; 44 | 88B9FE351F2439551342AEC4 /* Pods-CognitoApplication.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CognitoApplication.release.xcconfig"; path = "Pods/Target Support Files/Pods-CognitoApplication/Pods-CognitoApplication.release.xcconfig"; sourceTree = ""; }; 45 | /* End PBXFileReference section */ 46 | 47 | /* Begin PBXFrameworksBuildPhase section */ 48 | 632D251A1EB81732005A4E31 /* Frameworks */ = { 49 | isa = PBXFrameworksBuildPhase; 50 | buildActionMask = 2147483647; 51 | files = ( 52 | 6E3BB744AD21E1DD722F496E /* Pods_CognitoApplication.framework in Frameworks */, 53 | ); 54 | runOnlyForDeploymentPostprocessing = 0; 55 | }; 56 | /* End PBXFrameworksBuildPhase section */ 57 | 58 | /* Begin PBXGroup section */ 59 | 02715F221045F7A1A6B346C4 /* Pods */ = { 60 | isa = PBXGroup; 61 | children = ( 62 | 5D6FA8A57C007EC439FA41F7 /* Pods-CognitoApplication.debug.xcconfig */, 63 | 88B9FE351F2439551342AEC4 /* Pods-CognitoApplication.release.xcconfig */, 64 | ); 65 | name = Pods; 66 | sourceTree = ""; 67 | }; 68 | 632D25141EB81731005A4E31 = { 69 | isa = PBXGroup; 70 | children = ( 71 | 632D251F1EB81732005A4E31 /* CognitoApplication */, 72 | 632D251E1EB81732005A4E31 /* Products */, 73 | 02715F221045F7A1A6B346C4 /* Pods */, 74 | F5247A24A42C70CE5B467572 /* Frameworks */, 75 | ); 76 | sourceTree = ""; 77 | }; 78 | 632D251E1EB81732005A4E31 /* Products */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | 632D251D1EB81732005A4E31 /* CognitoApplication.app */, 82 | ); 83 | name = Products; 84 | sourceTree = ""; 85 | }; 86 | 632D251F1EB81732005A4E31 /* CognitoApplication */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 63A8F7D81F311C0400AD21E0 /* Extension */, 90 | 6398A66E1EC153F0009C2D77 /* Config */, 91 | 632D25331EB95CAB005A4E31 /* Views */, 92 | 632D25201EB81732005A4E31 /* AppDelegate.swift */, 93 | 632D25241EB81732005A4E31 /* Main.storyboard */, 94 | 632D25271EB81732005A4E31 /* Assets.xcassets */, 95 | 632D25291EB81732005A4E31 /* LaunchScreen.storyboard */, 96 | 632D252C1EB81732005A4E31 /* Info.plist */, 97 | ); 98 | path = CognitoApplication; 99 | sourceTree = ""; 100 | }; 101 | 632D25331EB95CAB005A4E31 /* Views */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | 63B5E4F21F33EBC300B9D1A3 /* MultiFactorAuthenticationController.swift */, 105 | 632D25341EB95CBD005A4E31 /* LoginViewController.swift */, 106 | 632D25361EB95CCE005A4E31 /* ResetPasswordViewController.swift */, 107 | 632D25381EB95CDE005A4E31 /* AppViewController.swift */, 108 | 63A8F7D41F30E79B00AD21E0 /* SignupViewController.swift */, 109 | 63A8F7D61F30FAC800AD21E0 /* VerificationViewController.swift */, 110 | 6373365D1F3252B700EA5BCE /* ForgotPasswordViewController.swift */, 111 | ); 112 | name = Views; 113 | sourceTree = ""; 114 | }; 115 | 6398A66E1EC153F0009C2D77 /* Config */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | 6398A66F1EC15413009C2D77 /* CognitoConfig.plist */, 119 | 6398A6711EC1548D009C2D77 /* CognitoConfig.swift */, 120 | ); 121 | name = Config; 122 | sourceTree = ""; 123 | }; 124 | 63A8F7D81F311C0400AD21E0 /* Extension */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | ); 128 | name = Extension; 129 | sourceTree = ""; 130 | }; 131 | F5247A24A42C70CE5B467572 /* Frameworks */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | 50439609D2FD5FDF1A0EA3E5 /* Pods_CognitoApplication.framework */, 135 | ); 136 | name = Frameworks; 137 | sourceTree = ""; 138 | }; 139 | /* End PBXGroup section */ 140 | 141 | /* Begin PBXNativeTarget section */ 142 | 632D251C1EB81732005A4E31 /* CognitoApplication */ = { 143 | isa = PBXNativeTarget; 144 | buildConfigurationList = 632D252F1EB81732005A4E31 /* Build configuration list for PBXNativeTarget "CognitoApplication" */; 145 | buildPhases = ( 146 | 873D1490F79AA8373D085D9F /* [CP] Check Pods Manifest.lock */, 147 | 632D25191EB81732005A4E31 /* Sources */, 148 | 632D251A1EB81732005A4E31 /* Frameworks */, 149 | 632D251B1EB81732005A4E31 /* Resources */, 150 | 82D36ECA54A64E276AEE3212 /* [CP] Embed Pods Frameworks */, 151 | 46911EFD3642422C02B746D8 /* [CP] Copy Pods Resources */, 152 | ); 153 | buildRules = ( 154 | ); 155 | dependencies = ( 156 | ); 157 | name = CognitoApplication; 158 | productName = CognitoApplication; 159 | productReference = 632D251D1EB81732005A4E31 /* CognitoApplication.app */; 160 | productType = "com.apple.product-type.application"; 161 | }; 162 | /* End PBXNativeTarget section */ 163 | 164 | /* Begin PBXProject section */ 165 | 632D25151EB81731005A4E31 /* Project object */ = { 166 | isa = PBXProject; 167 | attributes = { 168 | LastSwiftUpdateCheck = 0830; 169 | LastUpgradeCheck = 0830; 170 | ORGANIZATIONNAME = "David Tucker"; 171 | TargetAttributes = { 172 | 632D251C1EB81732005A4E31 = { 173 | CreatedOnToolsVersion = 8.3.2; 174 | DevelopmentTeam = 45HB25QHYT; 175 | ProvisioningStyle = Automatic; 176 | }; 177 | }; 178 | }; 179 | buildConfigurationList = 632D25181EB81732005A4E31 /* Build configuration list for PBXProject "CognitoApplication" */; 180 | compatibilityVersion = "Xcode 3.2"; 181 | developmentRegion = English; 182 | hasScannedForEncodings = 0; 183 | knownRegions = ( 184 | en, 185 | Base, 186 | ); 187 | mainGroup = 632D25141EB81731005A4E31; 188 | productRefGroup = 632D251E1EB81732005A4E31 /* Products */; 189 | projectDirPath = ""; 190 | projectRoot = ""; 191 | targets = ( 192 | 632D251C1EB81732005A4E31 /* CognitoApplication */, 193 | ); 194 | }; 195 | /* End PBXProject section */ 196 | 197 | /* Begin PBXResourcesBuildPhase section */ 198 | 632D251B1EB81732005A4E31 /* Resources */ = { 199 | isa = PBXResourcesBuildPhase; 200 | buildActionMask = 2147483647; 201 | files = ( 202 | 632D252B1EB81732005A4E31 /* LaunchScreen.storyboard in Resources */, 203 | 632D25281EB81732005A4E31 /* Assets.xcassets in Resources */, 204 | 6398A6701EC15413009C2D77 /* CognitoConfig.plist in Resources */, 205 | 632D25261EB81732005A4E31 /* Main.storyboard in Resources */, 206 | ); 207 | runOnlyForDeploymentPostprocessing = 0; 208 | }; 209 | /* End PBXResourcesBuildPhase section */ 210 | 211 | /* Begin PBXShellScriptBuildPhase section */ 212 | 46911EFD3642422C02B746D8 /* [CP] Copy Pods Resources */ = { 213 | isa = PBXShellScriptBuildPhase; 214 | buildActionMask = 2147483647; 215 | files = ( 216 | ); 217 | inputPaths = ( 218 | ); 219 | name = "[CP] Copy Pods Resources"; 220 | outputPaths = ( 221 | ); 222 | runOnlyForDeploymentPostprocessing = 0; 223 | shellPath = /bin/sh; 224 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-CognitoApplication/Pods-CognitoApplication-resources.sh\"\n"; 225 | showEnvVarsInLog = 0; 226 | }; 227 | 82D36ECA54A64E276AEE3212 /* [CP] Embed Pods Frameworks */ = { 228 | isa = PBXShellScriptBuildPhase; 229 | buildActionMask = 2147483647; 230 | files = ( 231 | ); 232 | inputPaths = ( 233 | ); 234 | name = "[CP] Embed Pods Frameworks"; 235 | outputPaths = ( 236 | ); 237 | runOnlyForDeploymentPostprocessing = 0; 238 | shellPath = /bin/sh; 239 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-CognitoApplication/Pods-CognitoApplication-frameworks.sh\"\n"; 240 | showEnvVarsInLog = 0; 241 | }; 242 | 873D1490F79AA8373D085D9F /* [CP] Check Pods Manifest.lock */ = { 243 | isa = PBXShellScriptBuildPhase; 244 | buildActionMask = 2147483647; 245 | files = ( 246 | ); 247 | inputPaths = ( 248 | ); 249 | name = "[CP] Check Pods Manifest.lock"; 250 | outputPaths = ( 251 | ); 252 | runOnlyForDeploymentPostprocessing = 0; 253 | shellPath = /bin/sh; 254 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; 255 | showEnvVarsInLog = 0; 256 | }; 257 | /* End PBXShellScriptBuildPhase section */ 258 | 259 | /* Begin PBXSourcesBuildPhase section */ 260 | 632D25191EB81732005A4E31 /* Sources */ = { 261 | isa = PBXSourcesBuildPhase; 262 | buildActionMask = 2147483647; 263 | files = ( 264 | 63B5E4F31F33EBC300B9D1A3 /* MultiFactorAuthenticationController.swift in Sources */, 265 | 6373365E1F3252B700EA5BCE /* ForgotPasswordViewController.swift in Sources */, 266 | 63A8F7D51F30E79B00AD21E0 /* SignupViewController.swift in Sources */, 267 | 632D25371EB95CCE005A4E31 /* ResetPasswordViewController.swift in Sources */, 268 | 6398A6721EC1548D009C2D77 /* CognitoConfig.swift in Sources */, 269 | 632D25351EB95CBD005A4E31 /* LoginViewController.swift in Sources */, 270 | 632D25391EB95CDE005A4E31 /* AppViewController.swift in Sources */, 271 | 63A8F7D71F30FAC800AD21E0 /* VerificationViewController.swift in Sources */, 272 | 632D25211EB81732005A4E31 /* AppDelegate.swift in Sources */, 273 | ); 274 | runOnlyForDeploymentPostprocessing = 0; 275 | }; 276 | /* End PBXSourcesBuildPhase section */ 277 | 278 | /* Begin PBXVariantGroup section */ 279 | 632D25241EB81732005A4E31 /* Main.storyboard */ = { 280 | isa = PBXVariantGroup; 281 | children = ( 282 | 632D25251EB81732005A4E31 /* Base */, 283 | ); 284 | name = Main.storyboard; 285 | sourceTree = ""; 286 | }; 287 | 632D25291EB81732005A4E31 /* LaunchScreen.storyboard */ = { 288 | isa = PBXVariantGroup; 289 | children = ( 290 | 632D252A1EB81732005A4E31 /* Base */, 291 | ); 292 | name = LaunchScreen.storyboard; 293 | sourceTree = ""; 294 | }; 295 | /* End PBXVariantGroup section */ 296 | 297 | /* Begin XCBuildConfiguration section */ 298 | 632D252D1EB81732005A4E31 /* Debug */ = { 299 | isa = XCBuildConfiguration; 300 | buildSettings = { 301 | ALWAYS_SEARCH_USER_PATHS = NO; 302 | CLANG_ANALYZER_NONNULL = YES; 303 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 304 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 305 | CLANG_CXX_LIBRARY = "libc++"; 306 | CLANG_ENABLE_MODULES = YES; 307 | CLANG_ENABLE_OBJC_ARC = YES; 308 | CLANG_WARN_BOOL_CONVERSION = YES; 309 | CLANG_WARN_CONSTANT_CONVERSION = YES; 310 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 311 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 312 | CLANG_WARN_EMPTY_BODY = YES; 313 | CLANG_WARN_ENUM_CONVERSION = YES; 314 | CLANG_WARN_INFINITE_RECURSION = YES; 315 | CLANG_WARN_INT_CONVERSION = YES; 316 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 317 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 318 | CLANG_WARN_UNREACHABLE_CODE = YES; 319 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 320 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 321 | COPY_PHASE_STRIP = NO; 322 | DEBUG_INFORMATION_FORMAT = dwarf; 323 | ENABLE_STRICT_OBJC_MSGSEND = YES; 324 | ENABLE_TESTABILITY = YES; 325 | GCC_C_LANGUAGE_STANDARD = gnu99; 326 | GCC_DYNAMIC_NO_PIC = NO; 327 | GCC_NO_COMMON_BLOCKS = YES; 328 | GCC_OPTIMIZATION_LEVEL = 0; 329 | GCC_PREPROCESSOR_DEFINITIONS = ( 330 | "DEBUG=1", 331 | "$(inherited)", 332 | ); 333 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 334 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 335 | GCC_WARN_UNDECLARED_SELECTOR = YES; 336 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 337 | GCC_WARN_UNUSED_FUNCTION = YES; 338 | GCC_WARN_UNUSED_VARIABLE = YES; 339 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 340 | MTL_ENABLE_DEBUG_INFO = YES; 341 | ONLY_ACTIVE_ARCH = YES; 342 | SDKROOT = iphoneos; 343 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 344 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 345 | }; 346 | name = Debug; 347 | }; 348 | 632D252E1EB81732005A4E31 /* Release */ = { 349 | isa = XCBuildConfiguration; 350 | buildSettings = { 351 | ALWAYS_SEARCH_USER_PATHS = NO; 352 | CLANG_ANALYZER_NONNULL = YES; 353 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 354 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 355 | CLANG_CXX_LIBRARY = "libc++"; 356 | CLANG_ENABLE_MODULES = YES; 357 | CLANG_ENABLE_OBJC_ARC = YES; 358 | CLANG_WARN_BOOL_CONVERSION = YES; 359 | CLANG_WARN_CONSTANT_CONVERSION = YES; 360 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 361 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 362 | CLANG_WARN_EMPTY_BODY = YES; 363 | CLANG_WARN_ENUM_CONVERSION = YES; 364 | CLANG_WARN_INFINITE_RECURSION = YES; 365 | CLANG_WARN_INT_CONVERSION = YES; 366 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 367 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 368 | CLANG_WARN_UNREACHABLE_CODE = YES; 369 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 370 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 371 | COPY_PHASE_STRIP = NO; 372 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 373 | ENABLE_NS_ASSERTIONS = NO; 374 | ENABLE_STRICT_OBJC_MSGSEND = YES; 375 | GCC_C_LANGUAGE_STANDARD = gnu99; 376 | GCC_NO_COMMON_BLOCKS = YES; 377 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 378 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 379 | GCC_WARN_UNDECLARED_SELECTOR = YES; 380 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 381 | GCC_WARN_UNUSED_FUNCTION = YES; 382 | GCC_WARN_UNUSED_VARIABLE = YES; 383 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 384 | MTL_ENABLE_DEBUG_INFO = NO; 385 | SDKROOT = iphoneos; 386 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 387 | VALIDATE_PRODUCT = YES; 388 | }; 389 | name = Release; 390 | }; 391 | 632D25301EB81732005A4E31 /* Debug */ = { 392 | isa = XCBuildConfiguration; 393 | baseConfigurationReference = 5D6FA8A57C007EC439FA41F7 /* Pods-CognitoApplication.debug.xcconfig */; 394 | buildSettings = { 395 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 396 | DEVELOPMENT_TEAM = 45HB25QHYT; 397 | INFOPLIST_FILE = CognitoApplication/Info.plist; 398 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 399 | PRODUCT_BUNDLE_IDENTIFIER = net.davidtucker.CognitoApplication; 400 | PRODUCT_NAME = "$(TARGET_NAME)"; 401 | SWIFT_VERSION = 3.0; 402 | }; 403 | name = Debug; 404 | }; 405 | 632D25311EB81732005A4E31 /* Release */ = { 406 | isa = XCBuildConfiguration; 407 | baseConfigurationReference = 88B9FE351F2439551342AEC4 /* Pods-CognitoApplication.release.xcconfig */; 408 | buildSettings = { 409 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 410 | DEVELOPMENT_TEAM = 45HB25QHYT; 411 | INFOPLIST_FILE = CognitoApplication/Info.plist; 412 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 413 | PRODUCT_BUNDLE_IDENTIFIER = net.davidtucker.CognitoApplication; 414 | PRODUCT_NAME = "$(TARGET_NAME)"; 415 | SWIFT_VERSION = 3.0; 416 | }; 417 | name = Release; 418 | }; 419 | /* End XCBuildConfiguration section */ 420 | 421 | /* Begin XCConfigurationList section */ 422 | 632D25181EB81732005A4E31 /* Build configuration list for PBXProject "CognitoApplication" */ = { 423 | isa = XCConfigurationList; 424 | buildConfigurations = ( 425 | 632D252D1EB81732005A4E31 /* Debug */, 426 | 632D252E1EB81732005A4E31 /* Release */, 427 | ); 428 | defaultConfigurationIsVisible = 0; 429 | defaultConfigurationName = Release; 430 | }; 431 | 632D252F1EB81732005A4E31 /* Build configuration list for PBXNativeTarget "CognitoApplication" */ = { 432 | isa = XCConfigurationList; 433 | buildConfigurations = ( 434 | 632D25301EB81732005A4E31 /* Debug */, 435 | 632D25311EB81732005A4E31 /* Release */, 436 | ); 437 | defaultConfigurationIsVisible = 0; 438 | defaultConfigurationName = Release; 439 | }; 440 | /* End XCConfigurationList section */ 441 | }; 442 | rootObject = 632D25151EB81731005A4E31 /* Project object */; 443 | } 444 | -------------------------------------------------------------------------------- /CognitoApplication.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CognitoApplication.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /CognitoApplication/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Created by David Tucker (davidtucker.net) on 5/4/17. 4 | // 5 | // Copyright (c) 2017 David Tucker 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import AWSCognitoIdentityProvider 28 | 29 | let userPoolID = "SampleUserPool" 30 | 31 | @UIApplicationMain 32 | class AppDelegate: UIResponder, UIApplicationDelegate { 33 | 34 | class func defaultUserPool() -> AWSCognitoIdentityUserPool { 35 | return AWSCognitoIdentityUserPool(forKey: userPoolID) 36 | } 37 | 38 | var window: UIWindow? 39 | 40 | var loginViewController: LoginViewController? 41 | 42 | var resetPasswordViewController: ResetPasswordViewController? 43 | 44 | var multiFactorAuthenticationController: MultiFactorAuthenticationController? 45 | 46 | var navigationController: UINavigationController? 47 | 48 | var cognitoConfig:CognitoConfig? 49 | 50 | var storyboard: UIStoryboard? { 51 | return UIStoryboard(name: "Main", bundle: nil) 52 | } 53 | 54 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 55 | // setup logging 56 | AWSDDLog.sharedInstance.logLevel = .verbose 57 | AWSDDLog.add(AWSDDTTYLogger.sharedInstance) 58 | 59 | // setup cognito config 60 | self.cognitoConfig = CognitoConfig() 61 | 62 | // setup cognito 63 | setupCognitoUserPool() 64 | 65 | // Override point for customization after application launch. 66 | return true 67 | } 68 | 69 | func setupCognitoUserPool() { 70 | let clientId:String = self.cognitoConfig!.getClientId() 71 | let poolId:String = self.cognitoConfig!.getPoolId() 72 | let clientSecret:String = self.cognitoConfig!.getClientSecret() 73 | let region:AWSRegionType = self.cognitoConfig!.getRegion() 74 | 75 | let serviceConfiguration:AWSServiceConfiguration = AWSServiceConfiguration(region: region, credentialsProvider: nil) 76 | let cognitoConfiguration:AWSCognitoIdentityUserPoolConfiguration = AWSCognitoIdentityUserPoolConfiguration(clientId: clientId, clientSecret: clientSecret, poolId: poolId) 77 | AWSCognitoIdentityUserPool.register(with: serviceConfiguration, userPoolConfiguration: cognitoConfiguration, forKey: userPoolID) 78 | let pool:AWSCognitoIdentityUserPool = AppDelegate.defaultUserPool() 79 | pool.delegate = self 80 | } 81 | 82 | } 83 | 84 | extension AppDelegate: AWSCognitoIdentityInteractiveAuthenticationDelegate { 85 | 86 | func startPasswordAuthentication() -> AWSCognitoIdentityPasswordAuthentication { 87 | if(self.navigationController == nil) { 88 | self.navigationController = self.window?.rootViewController as? UINavigationController 89 | } 90 | 91 | if(self.loginViewController == nil) { 92 | self.loginViewController = self.storyboard?.instantiateViewController(withIdentifier: "LoginViewController") as? LoginViewController 93 | } 94 | 95 | DispatchQueue.main.async { 96 | if(self.loginViewController!.isViewLoaded || self.loginViewController!.view.window == nil) { 97 | self.navigationController?.present(self.loginViewController!, animated: true, completion: nil) 98 | } 99 | } 100 | 101 | return self.loginViewController! 102 | } 103 | 104 | func startNewPasswordRequired() -> AWSCognitoIdentityNewPasswordRequired { 105 | if (self.resetPasswordViewController == nil) { 106 | self.resetPasswordViewController = self.storyboard?.instantiateViewController(withIdentifier: "ResetPasswordController") as? ResetPasswordViewController 107 | } 108 | 109 | DispatchQueue.main.async { 110 | if(self.resetPasswordViewController!.isViewLoaded || self.resetPasswordViewController!.view.window == nil) { 111 | self.navigationController?.present(self.resetPasswordViewController!, animated: true, completion: nil) 112 | } 113 | } 114 | 115 | return self.resetPasswordViewController! 116 | } 117 | 118 | func startMultiFactorAuthentication() -> AWSCognitoIdentityMultiFactorAuthentication { 119 | if (self.multiFactorAuthenticationController == nil) { 120 | self.multiFactorAuthenticationController = self.storyboard?.instantiateViewController(withIdentifier: "MultiFactorAuthenticationController") as? MultiFactorAuthenticationController 121 | } 122 | 123 | DispatchQueue.main.async { 124 | if(self.multiFactorAuthenticationController!.isViewLoaded || self.multiFactorAuthenticationController!.view.window == nil) { 125 | self.navigationController?.present(self.multiFactorAuthenticationController!, animated: true, completion: nil) 126 | } 127 | } 128 | 129 | return self.multiFactorAuthenticationController! 130 | } 131 | 132 | } 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /CognitoApplication/AppViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppViewController.swift 3 | // Created by David Tucker (davidtucker.net) on 5/4/17. 4 | // 5 | // Copyright (c) 2017 David Tucker 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import AWSCognitoIdentityProvider 28 | 29 | class AppViewController: UITableViewController { 30 | 31 | @IBOutlet weak var usernameLabel: UILabel! 32 | @IBOutlet weak var firstNameLabel: UILabel! 33 | @IBOutlet weak var lastNameLabel: UILabel! 34 | @IBOutlet weak var phoneNumberLabel: UILabel! 35 | @IBOutlet weak var mfaSwitch: UISwitch! 36 | 37 | var user:AWSCognitoIdentityUser? 38 | var userAttributes:[AWSCognitoIdentityProviderAttributeType]? 39 | var mfaSettings:[AWSCognitoIdentityProviderMFAOptionType]? 40 | 41 | override func viewDidLoad() { 42 | super.viewDidLoad() 43 | loadUserValues() 44 | } 45 | 46 | func loadUserValues () { 47 | self.resetAttributeValues() 48 | self.fetchUserAttributes() 49 | } 50 | 51 | func fetchUserAttributes() { 52 | self.resetAttributeValues() 53 | user = AppDelegate.defaultUserPool().currentUser() 54 | user?.getDetails().continueOnSuccessWith(block: { (task) -> Any? in 55 | guard task.result != nil else { 56 | return nil 57 | } 58 | self.userAttributes = task.result?.userAttributes 59 | self.mfaSettings = task.result?.mfaOptions 60 | self.userAttributes?.forEach({ (attribute) in 61 | print("Name: " + attribute.name!) 62 | }) 63 | DispatchQueue.main.async { 64 | self.setAttributeValues() 65 | } 66 | return nil 67 | }) 68 | } 69 | 70 | func resetAttributeValues() { 71 | DispatchQueue.main.async { 72 | self.lastNameLabel.text = "" 73 | self.firstNameLabel.text = "" 74 | self.usernameLabel.text = "" 75 | self.phoneNumberLabel.text = "" 76 | self.mfaSwitch.setOn(false, animated: false) 77 | } 78 | } 79 | 80 | @IBAction func handleSwitch(_ sender: AnyObject) { 81 | let settings = AWSCognitoIdentityUserSettings() 82 | if mfaSwitch.isOn { 83 | // Enable MFA 84 | let mfaOptions = AWSCognitoIdentityUserMFAOption() 85 | mfaOptions.attributeName = "phone_number" 86 | mfaOptions.deliveryMedium = .sms 87 | settings.mfaOptions = [mfaOptions] 88 | } else { 89 | // Disable MFA 90 | settings.mfaOptions = [] 91 | } 92 | user?.setUserSettings(settings) 93 | .continueOnSuccessWith(block: { (response) -> Any? in 94 | if response.error != nil { 95 | let alert = UIAlertController(title: "Error", message: (response.error! as NSError).userInfo["message"] as? String, preferredStyle: .alert) 96 | alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) 97 | self.present(alert, animated: true, completion:nil) 98 | self.resetAttributeValues() 99 | } else { 100 | self.fetchUserAttributes() 101 | } 102 | return nil 103 | }) 104 | 105 | } 106 | 107 | func isEmailMFAEnabled() -> Bool { 108 | let values = self.mfaSettings?.filter { $0.deliveryMedium == AWSCognitoIdentityProviderDeliveryMediumType.sms } 109 | if values?.first != nil { 110 | return true 111 | } 112 | return false 113 | } 114 | 115 | func setAttributeValues() { 116 | DispatchQueue.main.async { 117 | self.lastNameLabel.text = self.valueForAttribute(name: "family_name") 118 | self.firstNameLabel.text = self.valueForAttribute(name: "given_name") 119 | self.usernameLabel.text = self.valueForAttribute(name: "email") 120 | self.phoneNumberLabel.text = self.valueForAttribute(name: "phone_number") 121 | if self.mfaSettings == nil { 122 | self.mfaSwitch.setOn(false, animated: false) 123 | } else { 124 | self.mfaSwitch.setOn(self.isEmailMFAEnabled(), animated: false) 125 | } 126 | } 127 | } 128 | 129 | func valueForAttribute(name:String) -> String? { 130 | let values = self.userAttributes?.filter { $0.name == name } 131 | return values?.first?.value 132 | } 133 | 134 | @IBAction func logout(_ sender:AnyObject) { 135 | user?.signOut() 136 | self.fetchUserAttributes() 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /CognitoApplication/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | } 43 | ], 44 | "info" : { 45 | "version" : 1, 46 | "author" : "xcode" 47 | } 48 | } -------------------------------------------------------------------------------- /CognitoApplication/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 | -------------------------------------------------------------------------------- /CognitoApplication/Base.lproj/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 | 37 | 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 | 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 | 118 | 119 | 120 | 121 | 122 | 123 | 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 | 184 | 197 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 281 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 388 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 537 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 745 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 781 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 817 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 853 | 863 | 864 | 865 | 866 | 867 | 868 | 869 | 870 | 871 | 872 | 873 | 874 | 875 | 876 | 877 | 878 | 879 | 889 | 890 | 891 | 892 | 893 | 894 | 895 | 896 | 897 | 898 | 899 | 900 | 901 | 902 | 903 | 904 | 905 | 906 | 907 | 908 | 909 | 910 | 911 | 912 | 913 | 914 | 915 | 916 | 917 | 918 | 919 | 920 | 921 | 922 | 923 | 924 | 925 | 926 | 927 | 928 | 929 | 930 | 931 | -------------------------------------------------------------------------------- /CognitoApplication/CognitoConfig.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CognitoConfig.swift 3 | // Created by David Tucker (davidtucker.net) on 5/4/17. 4 | // 5 | // Copyright (c) 2017 David Tucker 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import Foundation 27 | import AWSCore 28 | 29 | class CognitoConfig: NSObject { 30 | 31 | var keys:Dictionary? 32 | 33 | override init() { 34 | super.init() 35 | self.keys = readPropertyList() 36 | guard self.keys != nil else { 37 | fatalError("You must include a CognitoConfig.plist file with the necesary values for your user pool") 38 | } 39 | } 40 | 41 | func readPropertyList() -> Dictionary? { 42 | if let path = Bundle.main.path(forResource: "CognitoConfig", ofType: "plist") { 43 | let keys = NSDictionary(contentsOfFile: path) 44 | return keys as? Dictionary 45 | } 46 | return nil 47 | } 48 | 49 | func getPoolId() -> String { 50 | let poolId = self.keys?["poolId"] as? String 51 | guard poolId != nil else { 52 | fatalError("You must specify a poolId in your CognitoConfig.plist file") 53 | } 54 | return poolId! 55 | } 56 | 57 | func getClientId() -> String { 58 | let clientId = self.keys?["clientId"] as? String 59 | guard clientId != nil else { 60 | fatalError("You must specify a clientlId in your CognitoConfig.plist file") 61 | } 62 | return clientId! 63 | } 64 | 65 | func getClientSecret() -> String { 66 | let clientSecret = self.keys?["clientSecret"] as? String 67 | guard clientSecret != nil else { 68 | fatalError("You must specify a clientSecret in your CognitoConfig.plist file") 69 | } 70 | return clientSecret! 71 | } 72 | 73 | func getRegion() -> AWSRegionType { 74 | let region = self.keys?["region"] as? String 75 | guard region != nil else { 76 | fatalError("You must specify a region value in CognitoConfig.plist") 77 | } 78 | var output:AWSRegionType? 79 | 80 | switch region! { 81 | 82 | case "us-east-1": 83 | output = AWSRegionType.USEast1 84 | case "us-east-2": 85 | output = AWSRegionType.USEast2 86 | case "us-west-1": 87 | output = AWSRegionType.USWest1 88 | case "us-west-2": 89 | output = AWSRegionType.USWest2 90 | case "ap-south-1": 91 | output = AWSRegionType.APSouth1 92 | case "ap-northeast-1": 93 | output = AWSRegionType.APNortheast1 94 | case "ap-northeast-2": 95 | output = AWSRegionType.APNortheast2 96 | case "ap-southeast-1": 97 | output = AWSRegionType.APSoutheast1 98 | case "ap-southeast-2": 99 | output = AWSRegionType.APSoutheast2 100 | case "ca-central-1": 101 | output = AWSRegionType.CACentral1 102 | case "cn-north-1": 103 | output = AWSRegionType.CNNorth1 104 | case "eu-central-1": 105 | output = AWSRegionType.EUCentral1 106 | case "eu-west-1": 107 | output = AWSRegionType.EUWest1 108 | case "eu-west-2": 109 | output = AWSRegionType.EUWest2 110 | case "sa-east-1": 111 | output = AWSRegionType.SAEast1 112 | case "us-gov-west-1": 113 | output = AWSRegionType.USGovWest1 114 | default: 115 | print("Invalid region specified") 116 | 117 | } 118 | 119 | guard output != nil else { 120 | fatalError("You must specify a valid region value in CognitoConfig.plist such as 'us-east-1'") 121 | } 122 | 123 | return output! 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /CognitoApplication/ForgotPasswordViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ForgotPasswordViewController.swift 3 | // CognitoApplication 4 | // 5 | // Created by David Tucker on 8/2/17. 6 | // Copyright © 2017 David Tucker. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AWSCognitoIdentityProvider 11 | 12 | class ForgotPasswordViewController: UIViewController { 13 | 14 | @IBOutlet weak var verificationCode: UITextField! 15 | @IBOutlet weak var newPassword: UITextField! 16 | @IBOutlet weak var confirmPassword: UITextField! 17 | @IBOutlet weak var resetPasswordButton: UIButton! 18 | 19 | var emailAddress:String = "" 20 | var user:AWSCognitoIdentityUser? 21 | 22 | func clearFields() { 23 | self.verificationCode.text = "" 24 | self.newPassword.text = "" 25 | self.confirmPassword.text = "" 26 | self.emailAddress = "" 27 | } 28 | 29 | override func viewDidAppear(_ animated: Bool) { 30 | super.viewDidAppear(animated) 31 | if !emailAddress.isEmpty { 32 | let pool = AppDelegate.defaultUserPool() 33 | // Get a reference to the user using the email address 34 | user = pool.getUser(emailAddress) 35 | // Initiate the forgot password process which will send a verification code to the user 36 | user?.forgotPassword() 37 | .continueWith(block: { (response) -> Any? in 38 | if response.error != nil { 39 | // Cannot request password reset due to error (for example, the attempt limit exceeded) 40 | let alert = UIAlertController(title: "Cannot Reset Password", message: (response.error! as NSError).userInfo["message"] as? String, preferredStyle: .alert) 41 | alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in 42 | self.clearFields() 43 | self.presentingViewController?.dismiss(animated: true, completion: nil) 44 | })) 45 | self.present(alert, animated: true, completion: nil) 46 | return nil 47 | } 48 | // Password reset was requested and message sent. Let the user know where to look for code. 49 | let result = response.result 50 | let isEmail = (result?.codeDeliveryDetails?.deliveryMedium == AWSCognitoIdentityProviderDeliveryMediumType.email) 51 | let destination:String = result!.codeDeliveryDetails!.destination! 52 | let medium = isEmail ? "an email" : "a text message" 53 | let alert = UIAlertController(title: "Verification Sent", message: "You should receive \(medium) with a verification code at \(destination). Enter that code here along with a new password.", preferredStyle: .alert) 54 | alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) 55 | self.present(alert, animated: true, completion: nil) 56 | return nil 57 | }) 58 | } 59 | } 60 | 61 | @IBAction func resetPasswordPressed(_ sender: AnyObject) { 62 | user?.confirmForgotPassword(self.verificationCode.text!, password: self.newPassword.text!) 63 | .continueWith { (response) -> Any? in 64 | if response.error != nil { 65 | // The password could not be reset - let the user know 66 | let alert = UIAlertController(title: "Cannot Reset Password", message: (response.error! as NSError).userInfo["message"] as? String, preferredStyle: .alert) 67 | alert.addAction(UIAlertAction(title: "Resend Code", style: .default, handler: { (action) in 68 | self.user?.forgotPassword() 69 | .continueWith(block: { (result) -> Any? in 70 | print("Code Sent") 71 | return nil 72 | }) 73 | })) 74 | alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { (action) in 75 | DispatchQueue.main.async { 76 | self.presentingViewController?.dismiss(animated: true, completion: nil) 77 | } 78 | })) 79 | DispatchQueue.main.async { 80 | self.present(alert, animated: true, completion: nil) 81 | } 82 | } else { 83 | // Password reset. Send the user back to the login and let them know they can login with new password. 84 | DispatchQueue.main.async { 85 | let presentingController = self.presentingViewController 86 | self.presentingViewController?.dismiss(animated: true, completion: { 87 | let alert = UIAlertController(title: "Password Reset", message: "Password reset. Please log into the account with your email and new password.", preferredStyle: .alert) 88 | alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) 89 | presentingController?.present(alert, animated: true, completion: nil) 90 | self.clearFields() 91 | } 92 | ) 93 | } 94 | } 95 | return nil 96 | } 97 | } 98 | 99 | @IBAction func cancelForgotPasswordPressed(_ sender: AnyObject) { 100 | clearFields() 101 | self.presentingViewController?.dismiss(animated: true, completion: nil) 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /CognitoApplication/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 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /CognitoApplication/LoginViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoginViewController.swift 3 | // Created by David Tucker (davidtucker.net) on 5/4/17. 4 | // 5 | // Copyright (c) 2017 David Tucker 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import AWSCognitoIdentityProvider 28 | 29 | class LoginViewController: UIViewController { 30 | 31 | @IBOutlet weak var passwordInput: UITextField? 32 | @IBOutlet weak var usernameInput: UITextField? 33 | @IBOutlet weak var loginButton: UIButton? 34 | 35 | var passwordAuthenticationCompletion: AWSTaskCompletionSource? 36 | 37 | override func viewWillAppear(_ animated: Bool) { 38 | super.viewWillAppear(animated) 39 | self.passwordInput?.addTarget(self, action: #selector(inputDidChange(_:)), for: .editingChanged) 40 | self.usernameInput?.addTarget(self, action: #selector(inputDidChange(_:)), for: .editingChanged) 41 | } 42 | 43 | @IBAction func loginPressed(_ sender: AnyObject) { 44 | if (self.usernameInput?.text == nil || self.passwordInput?.text == nil) { 45 | return 46 | } 47 | 48 | let authDetails = AWSCognitoIdentityPasswordAuthenticationDetails(username: self.usernameInput!.text!, password: self.passwordInput!.text! ) 49 | self.passwordAuthenticationCompletion?.set(result: authDetails) 50 | } 51 | 52 | @IBAction func forgotPasswordPressed(_ sender: AnyObject) { 53 | if (self.usernameInput?.text == nil || self.usernameInput!.text!.isEmpty) { 54 | let alertController = UIAlertController(title: "Enter Username", 55 | message: "Please enter your username and then select Forgot Password if you want to reset your password.", 56 | preferredStyle: .alert) 57 | let retryAction = UIAlertAction(title: "OK", style: .default, handler: nil) 58 | alertController.addAction(retryAction) 59 | self.present(alertController, animated: true, completion: nil) 60 | return 61 | } 62 | self.performSegue(withIdentifier: "ForgotPasswordSegue", sender: self) 63 | } 64 | 65 | func inputDidChange(_ sender:AnyObject) { 66 | if (self.usernameInput?.text != nil && self.passwordInput?.text != nil) { 67 | self.loginButton?.isEnabled = true 68 | } else { 69 | self.loginButton?.isEnabled = false 70 | } 71 | } 72 | 73 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 74 | if segue.identifier == "ForgotPasswordSegue" { 75 | let forgotPasswordController = segue.destination as! ForgotPasswordViewController 76 | forgotPasswordController.emailAddress = self.usernameInput!.text! 77 | } 78 | } 79 | 80 | } 81 | 82 | extension LoginViewController: AWSCognitoIdentityPasswordAuthentication { 83 | 84 | public func getDetails(_ authenticationInput: AWSCognitoIdentityPasswordAuthenticationInput, passwordAuthenticationCompletionSource: AWSTaskCompletionSource) { 85 | self.passwordAuthenticationCompletion = passwordAuthenticationCompletionSource 86 | DispatchQueue.main.async { 87 | if (self.usernameInput?.text == nil) { 88 | self.usernameInput?.text = authenticationInput.lastKnownUsername 89 | } 90 | } 91 | } 92 | 93 | public func didCompleteStepWithError(_ error: Error?) { 94 | DispatchQueue.main.async { 95 | if error != nil { 96 | let alertController = UIAlertController(title: "Cannot Login", 97 | message: (error! as NSError).userInfo["message"] as? String, 98 | preferredStyle: .alert) 99 | let retryAction = UIAlertAction(title: "Retry", style: .default, handler: nil) 100 | alertController.addAction(retryAction) 101 | 102 | self.present(alertController, animated: true, completion: nil) 103 | } else { 104 | self.dismiss(animated: true, completion: { 105 | self.usernameInput?.text = nil 106 | self.passwordInput?.text = nil 107 | }) 108 | } 109 | } 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /CognitoApplication/MultiFactorAuthenticationController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MultiFactorAuthenticationController.swift 3 | // CognitoApplication 4 | // 5 | // Created by David Tucker on 8/1/17. 6 | // Copyright © 2017 David Tucker. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AWSCognitoIdentityProvider 11 | 12 | class MultiFactorAuthenticationController: UIViewController { 13 | 14 | @IBOutlet weak var authenticationCode: UITextField! 15 | @IBOutlet weak var submitCodeButton: UIButton! 16 | 17 | var mfaCompletionSource:AWSTaskCompletionSource? 18 | 19 | @IBAction func submitCodePressed(_ sender: AnyObject) { 20 | self.mfaCompletionSource?.set(result: NSString(string: authenticationCode.text!)) 21 | } 22 | 23 | } 24 | 25 | extension MultiFactorAuthenticationController: AWSCognitoIdentityMultiFactorAuthentication { 26 | 27 | func getCode(_ authenticationInput: AWSCognitoIdentityMultifactorAuthenticationInput, mfaCodeCompletionSource: AWSTaskCompletionSource) { 28 | self.mfaCompletionSource = mfaCodeCompletionSource 29 | } 30 | 31 | func didCompleteMultifactorAuthenticationStepWithError(_ error: Error?) { 32 | DispatchQueue.main.async { 33 | self.authenticationCode.text = "" 34 | } 35 | if error != nil { 36 | let alertController = UIAlertController(title: "Cannot Verify Code", 37 | message: (error! as NSError).userInfo["message"] as? String, 38 | preferredStyle: .alert) 39 | let resendAction = UIAlertAction(title: "Try Again", style: .default, handler:nil) 40 | alertController.addAction(resendAction) 41 | 42 | let logoutAction = UIAlertAction(title: "Logout", style: .cancel, handler: { (action) in 43 | AppDelegate.defaultUserPool().currentUser()?.signOut() 44 | self.dismiss(animated: true, completion: { 45 | self.authenticationCode.text = nil 46 | }) 47 | }) 48 | alertController.addAction(logoutAction) 49 | 50 | self.present(alertController, animated: true, completion: nil) 51 | } else { 52 | self.dismiss(animated: true, completion: nil) 53 | } 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /CognitoApplication/ResetPasswordViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ResetPasswordViewController.swift 3 | // Created by David Tucker (davidtucker.net) on 5/4/17. 4 | // 5 | // Copyright (c) 2017 David Tucker 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import Foundation 27 | import UIKit 28 | import AWSCognitoIdentityProvider 29 | 30 | class ResetPasswordViewController: UIViewController { 31 | 32 | @IBOutlet weak var newPasswordButton: UIButton! 33 | @IBOutlet weak var newPasswordInput: UITextField! 34 | @IBOutlet weak var firstNameInput: UITextField! 35 | @IBOutlet weak var lastNameInput: UITextField! 36 | 37 | var currentUserAttributes:[String:String]? 38 | 39 | var resetPasswordCompletion: AWSTaskCompletionSource? 40 | 41 | override func viewWillAppear(_ animated: Bool) { 42 | super.viewWillAppear(animated) 43 | self.newPasswordInput.text = nil 44 | self.newPasswordInput.addTarget(self, action: #selector(inputDidChange(_:)), for: .editingChanged) 45 | self.firstNameInput.addTarget(self, action: #selector(inputDidChange(_:)), for: .editingChanged) 46 | self.lastNameInput.addTarget(self, action: #selector(inputDidChange(_:)), for: .editingChanged) 47 | } 48 | 49 | func inputDidChange(_ sender:AnyObject) { 50 | if (self.newPasswordInput.text != nil && self.firstNameInput != nil && self.lastNameInput != nil) { 51 | self.newPasswordButton.isEnabled = true 52 | } else { 53 | self.newPasswordButton.isEnabled = false 54 | } 55 | } 56 | 57 | @IBAction func submitNewPassword(_ sender:AnyObject) { 58 | var userAttributes:[String:String] = [:] 59 | userAttributes["family_name"] = self.lastNameInput.text 60 | userAttributes["given_name"] = self.firstNameInput.text 61 | let details = AWSCognitoIdentityNewPasswordRequiredDetails(proposedPassword: self.newPasswordInput.text!, userAttributes: userAttributes) 62 | self.resetPasswordCompletion?.set(result: details) 63 | } 64 | 65 | } 66 | 67 | extension ResetPasswordViewController: AWSCognitoIdentityNewPasswordRequired { 68 | 69 | public func getNewPasswordDetails(_ newPasswordRequiredInput: AWSCognitoIdentityNewPasswordRequiredInput, newPasswordRequiredCompletionSource: AWSTaskCompletionSource) { 70 | self.currentUserAttributes = newPasswordRequiredInput.userAttributes 71 | self.resetPasswordCompletion = newPasswordRequiredCompletionSource 72 | } 73 | 74 | public func didCompleteNewPasswordStepWithError(_ error: Error?) { 75 | DispatchQueue.main.async { 76 | if let error = error as NSError? { 77 | let alertController = UIAlertController(title: error.userInfo["__type"] as? String, 78 | message: error.userInfo["message"] as? String, 79 | preferredStyle: .alert) 80 | let retryAction = UIAlertAction(title: "Retry", style: .default, handler: nil) 81 | alertController.addAction(retryAction) 82 | self.present(alertController, animated: true, completion: nil) 83 | } else { 84 | self.newPasswordInput.text = nil 85 | self.dismiss(animated: true, completion: nil) 86 | } 87 | } 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /CognitoApplication/SignupViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SignupViewController.swift 3 | // 4 | // Copyright (c) 2017 David Tucker 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | 25 | import UIKit 26 | import AWSCognitoIdentityProvider 27 | 28 | class SignupViewController : UIViewController { 29 | 30 | @IBOutlet weak var firstName: UITextField! 31 | @IBOutlet weak var lastName: UITextField! 32 | @IBOutlet weak var email: UITextField! 33 | @IBOutlet weak var password: UITextField! 34 | @IBOutlet weak var confirmPassword: UITextField! 35 | @IBOutlet weak var submitButton: UIButton! 36 | @IBOutlet weak var phoneNumber: UITextField! 37 | 38 | var user: AWSCognitoIdentityUser? 39 | var codeDeliveryDetails:AWSCognitoIdentityProviderCodeDeliveryDetailsType? 40 | 41 | override func viewWillAppear(_ animated: Bool) { 42 | super.viewWillAppear(animated) 43 | self.submitButton.isEnabled = false 44 | self.firstName.addTarget(self, action: #selector(inputDidChange(_:)), for: .editingChanged) 45 | self.lastName.addTarget(self, action: #selector(inputDidChange(_:)), for: .editingChanged) 46 | self.email.addTarget(self, action: #selector(inputDidChange(_:)), for: .editingChanged) 47 | self.password.addTarget(self, action: #selector(inputDidChange(_:)), for: .editingChanged) 48 | self.confirmPassword.addTarget(self, action: #selector(inputDidChange(_:)), for: .editingChanged) 49 | self.phoneNumber.addTarget(self, action: #selector(inputDidChange(_:)), for: .editingChanged) 50 | } 51 | 52 | func inputDidChange(_ sender:AnyObject) { 53 | if(firstName.text == nil || lastName.text == nil) { 54 | self.submitButton.isEnabled = false 55 | return 56 | } 57 | if(email.text == nil) { 58 | self.submitButton.isEnabled = false 59 | return 60 | } 61 | if(password.text == nil || confirmPassword.text == nil) { 62 | self.submitButton.isEnabled = false 63 | return 64 | } 65 | if phoneNumber.text == nil || phoneNumber.text!.isEmpty == true { 66 | self.submitButton.isEnabled = false 67 | return 68 | } 69 | self.submitButton.isEnabled = (password.text == confirmPassword.text) 70 | } 71 | 72 | @IBAction func signupPressed(_ sender: AnyObject) { 73 | let userPool = AppDelegate.defaultUserPool() 74 | let emailAttribute = AWSCognitoIdentityUserAttributeType(name: "email", value: email.text!) 75 | let firstNameAttribute = AWSCognitoIdentityUserAttributeType(name: "given_name", value: firstName.text!) 76 | let lastNameAttribute = AWSCognitoIdentityUserAttributeType(name: "family_name", value: lastName.text!) 77 | let phoneNumberAttribute = AWSCognitoIdentityUserAttributeType(name: "phone_number", value: phoneNumber.text!) 78 | let attributes:[AWSCognitoIdentityUserAttributeType] = [emailAttribute, firstNameAttribute, lastNameAttribute, phoneNumberAttribute] 79 | userPool.signUp(email.text!, password: password.text!, userAttributes: attributes, validationData: nil) 80 | .continueWith { (response) -> Any? in 81 | if response.error != nil { 82 | // Error in the Signup Process 83 | let alert = UIAlertController(title: "Error", message: (response.error! as NSError).userInfo["message"] as? String, preferredStyle: .alert) 84 | alert.addAction(UIAlertAction(title: "OK", style: .default, handler:nil)) 85 | self.present(alert, animated: true, completion: nil) 86 | } else { 87 | self.user = response.result!.user 88 | // Does user need confirmation? 89 | if (response.result?.userConfirmed?.intValue != AWSCognitoIdentityUserStatus.confirmed.rawValue) { 90 | // User needs confirmation, so we need to proceed to the verify view controller 91 | DispatchQueue.main.async { 92 | self.codeDeliveryDetails = response.result?.codeDeliveryDetails 93 | self.performSegue(withIdentifier: "VerifySegue", sender: self) 94 | } 95 | } else { 96 | // User signed up but does not need confirmation. This should rarely happen (if ever). 97 | DispatchQueue.main.async { 98 | self.presentingViewController?.dismiss(animated: true, completion: nil) 99 | } 100 | } 101 | } 102 | return nil 103 | } 104 | } 105 | 106 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 107 | let verificationController = segue.destination as! VerificationViewController 108 | verificationController.codeDeliveryDetails = self.codeDeliveryDetails 109 | verificationController.user = self.user! 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /CognitoApplication/VerificationViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VerificationViewController.swift 3 | // 4 | // Copyright (c) 2017 David Tucker 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | import UIKit 25 | import AWSCognitoIdentityProvider 26 | 27 | class VerificationViewController: UIViewController { 28 | 29 | @IBOutlet weak var verificationField: UITextField! 30 | @IBOutlet weak var verifyButton: UIButton! 31 | @IBOutlet weak var verificationLabel: UILabel! 32 | 33 | var codeDeliveryDetails:AWSCognitoIdentityProviderCodeDeliveryDetailsType? 34 | 35 | var user: AWSCognitoIdentityUser? 36 | 37 | override func viewWillAppear(_ animated: Bool) { 38 | super.viewWillAppear(animated) 39 | self.verifyButton.isEnabled = false 40 | self.verificationField.addTarget(self, action: #selector(inputDidChange(_:)), for: .editingChanged) 41 | populateCodeDeliveryDetails() 42 | } 43 | 44 | func populateCodeDeliveryDetails() { 45 | let isEmail = (codeDeliveryDetails?.deliveryMedium == AWSCognitoIdentityProviderDeliveryMediumType.email) 46 | verifyButton.setTitle(isEmail ? "Verify Email Address" : "Verify Phone Number", for: .normal) 47 | let medium = isEmail ? "your email address" : "your phone number" 48 | let destination = codeDeliveryDetails!.destination! 49 | verificationLabel.text = "Please enter the code that was sent to \(medium) at \(destination)" 50 | } 51 | 52 | func inputDidChange(_ sender:AnyObject) { 53 | if(verificationField.text == nil) { 54 | self.verifyButton.isEnabled = false 55 | return 56 | } 57 | self.verifyButton.isEnabled = true 58 | } 59 | 60 | func resetConfirmation(message:String? = "") { 61 | self.verificationField.text = "" 62 | let alert = UIAlertController(title: "Error", message: message, preferredStyle: .alert) 63 | alert.addAction(UIAlertAction(title: "Retry", style: .default, handler: nil)) 64 | self.present(alert, animated: true, completion:nil) 65 | } 66 | 67 | @IBAction func verifyPressed(_ sender: AnyObject) { 68 | self.user?.confirmSignUp(verificationField.text!) 69 | .continueWith(block: { (response) -> Any? in 70 | if response.error != nil { 71 | self.resetConfirmation(message: (response.error! as NSError).userInfo["message"] as? String) 72 | } else { 73 | DispatchQueue.main.async { 74 | // Return to Login View Controller - this should be handled a bit differently, but added in this manner for simplicity 75 | self.presentingViewController?.presentingViewController?.dismiss(animated: true, completion: nil) 76 | } 77 | } 78 | return nil 79 | }) 80 | } 81 | 82 | @IBAction func resendConfirmationCodePressed(_ sender: AnyObject) { 83 | self.user?.resendConfirmationCode() 84 | .continueWith(block: { (respone) -> Any? in 85 | let alert = UIAlertController(title: "Resent", message: "The confirmation code has been resent.", preferredStyle: .alert) 86 | alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) 87 | self.present(alert, animated: true, completion:nil) 88 | return nil 89 | }) 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 David Tucker 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/CocoaPods/Specs.git' 2 | 3 | platform :ios, '10.0' 4 | use_frameworks! 5 | 6 | target 'CognitoApplication' do 7 | pod 'AWSCore', '~> 2.5.5' 8 | pod 'AWSCognitoIdentityProvider', '~> 2.5.5' 9 | end -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cognito User Pools iOS Example Application 2 | 3 | This application is a sample iOS application created to showcase how to integrate iOS applications with [AWS Cognito User Pools](http://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools.html) (which are a part of the [AWS Cognito](https://aws.amazon.com/cognito/) service). 4 | 5 | This code sample will be utilized in a series of articles which will expalain the integration. I will include links to the articles as they are published. 6 | 7 | ## Setup 8 | 9 | To run the application, you will have to perform the following steps: installing the AWS dependencies, setting up the user pool configuration, and creating a sample user. 10 | 11 | ### Installing Dependencies 12 | 13 | This application utilizes [CocoaPods](https://cocoapods.org/) for managing the dependencies. At this point, the only dependencies are the specific pieces of the AWS iOS SDK which relate to Cognito User Pools. 14 | 15 | If you haven't used CocoaPods, be sure to read the [Getting Started Guide](https://guides.cocoapods.org/using/getting-started.html). Once you have CocoaPods installed, just navigate to the project directory in your terminal (the one which contains the Podfile file) and enter the following: 16 | 17 | ```bash 18 | pod install 19 | ``` 20 | 21 | This will install the needed dependencies for this project. 22 | 23 | ### User Pool Configuration 24 | 25 | To use this example application, you will need to create a Cognito User Pool and add in four specific values to a config file. The config file should be named `CognitoApplication/CognitoConfig.plist`. This file should have the following keys: 26 | 27 | | Key | Type | Value | 28 | |----|----|----| 29 | | **region** | String| This is the region in which you created your user pool. This needs to be the standard region identifier such as 'us-east-1' or 'ap-southeast-1' | 30 | | **poolId** | String | The id of the user pool that you created | 31 | | **clientId** | String | The clientId configured as a part of the app that you attached to the user pool | 32 | | **clientSecret** | String | The clientSecret that is configured as a part of the app that you attached to the user pool | 33 | 34 | ### Creating a Sample User 35 | 36 | This initial version of the application does not include user signup (that will be handled in the second article). This requires that you setup a user in the Cognito console. For this to work as expected, the `given_name`, `family_name`, and `email` should be the only required attributes. 37 | --------------------------------------------------------------------------------