iOS(Swift)应用程序中的AWS Cognito用户池

我试图在我的iOS(Swift)应用程序中实现新的AWS Cognito用户池,但是我正努力使login过程正常工作。 我基本上试图按照这里提供的例子。

这是我迄今为止:

AppDelegate中:

class AppDelegate: UIResponder, UIApplicationDelegate, AWSCognitoIdentityInteractiveAuthenticationDelegate { func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { let serviceConfiguration = AWSServiceConfiguration(region: AWSRegionType.USEast1, credentialsProvider: nil) AWSServiceManager.defaultServiceManager().defaultServiceConfiguration = serviceConfiguration let configurationUserPool = AWSCognitoIdentityUserPoolConfiguration( clientId: "###", clientSecret: "#########", poolId: "###") AWSCognitoIdentityUserPool.registerCognitoIdentityUserPoolWithConfiguration(serviceConfiguration, userPoolConfiguration: configurationUserPool, forKey: "UserPool") self.userPool = AWSCognitoIdentityUserPool(forKey: "UserPool") self.userPool!.delegate = self return true } func startPasswordAuthentication() -> AWSCognitoIdentityPasswordAuthentication { let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil) let logInNavigationController = mainStoryboard.instantiateViewControllerWithIdentifier("LogInNavigationController") as! UINavigationController dispatch_async(dispatch_get_main_queue(), { self.window?.rootViewController = logInNavigationController }) let logInViewController = mainStoryboard.instantiateViewControllerWithIdentifier("LogInViewController") as! LogInViewController return logInViewController } } 

LogInViewController:

 class LogInViewController: UIViewController, AWSCognitoIdentityPasswordAuthentication { var usernameText : String? var passwordAuthenticationCompletion = AWSTaskCompletionSource() func getPasswordAuthenticationDetails(authenticationInput: AWSCognitoIdentityPasswordAuthenticationInput, passwordAuthenticationCompletionSource: AWSTaskCompletionSource) { self.passwordAuthenticationCompletion = passwordAuthenticationCompletionSource dispatch_async(dispatch_get_main_queue(), { if self.usernameText == nil { self.usernameText = authenticationInput.lastKnownUsername } }) } func didCompletePasswordAuthenticationStepWithError(error: NSError) { dispatch_async(dispatch_get_main_queue(), { let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil) let mainNavigationController = mainStoryboard.instantiateViewControllerWithIdentifier("MainNavigationController") as! UINavigationController (UIApplication.sharedApplication().delegate as! AppDelegate).window?.rootViewController = mainNavigationController }) } func logInButtonPressed() { self.passwordAuthenticationCompletion.setResult(AWSCognitoIdentityPasswordAuthenticationDetails(username: emailTextField.text, password: passwordTextField.text)) } } 

当我点击loginbutton时似乎没有什么事情发生,但是如果我再次打它,我得到一个NSInternalInconsistencyException(我相信是因为AWSTask结果已经设置)。

任何帮助,这将不胜感激。 我正在使用AWS SDK for iOS版本2.4.1。

更新:

不是我原来的问题的解决scheme,但我已经能够通过使用明确的login方法,而不是委托方法(请参阅此页的详细信息),以获得用户池的工作。 这里是我的SignInViewController的代码:

 class SignInViewController: UIViewController { @IBAction func signInButtonTouched(sender: UIButton) { if (emailTextField.text != nil) && (passwordTextField.text != nil) { let user = (UIApplication.sharedApplication().delegate as! AppDelegate).userPool!.getUser(emailTextField.text!) user.getSession(emailTextField.text!, password: passwordTextField.text!, validationData: nil, scopes: nil).continueWithExecutor(AWSExecutor.mainThreadExecutor(), withBlock: { (task:AWSTask!) -> AnyObject! in if task.error == nil { // user is logged in - show logged in UI } else { // error } return nil }) } else { // email or password not set } } } 

然后,为了使用AWS服务(在我的情况下,它位于与Cognito不同的区域),我使用用户池创build了一个新的凭证提供程序:

 let credentialsProvider = AWSCognitoCredentialsProvider(regionType: .USEast1, identityPoolId: "###", identityProviderManager: (UIApplication.sharedApplication().delegate as! AppDelegate).userPool!) let serviceConfiguration = AWSServiceConfiguration(region: .APNortheast1, credentialsProvider: credentialsProvider) AWSLambdaInvoker.registerLambdaInvokerWithConfiguration(serviceConfiguration, forKey: "Lambda") let lambdaInvoker = AWSLambdaInvoker(forKey: "Lambda") 

另外一个问题是,我每次启动应用程序时都会看到这个错误:“无法在info.plist中find有效的”AWSDefaultRegionType“,”AWSCognitoRegionType“和”AWSCognitoIdentityPoolId“值。 这似乎与织物有关,我正在使用它来跟踪崩溃。 我已经通过在AppDelegate中更改此行来解决此问题:

 Fabric.with([AWSCognito.self, Crashlytics.self]) 

对此:

 Fabric.with([Crashlytics.self]) 

我希望这可以帮助别人。

更新6 :(这次真的是最后的)

值得一提的是,(最终)AWS已经使AWS Mobile Hub构build了一个非常漂亮的演示应用程序,包括用户池作为SignInProvider(也包括Google和Facebook)。 这个架构(在我看来)非常出色(他们已经分离了身份pipe理并从身份validation中获取证书)

更新5:(和最终)

有一个相当完整的示例实现,以及在这个其他答案中如何工作的一些文档。

iOS – AWS MobileHub使用开发人员身份validation的提供程序进行login

更新4:

如果您想访问AWS服务,则需要执行更多步骤

事实certificate,这不会让您使用Cognito Federated Identities进行身份validation(身份浏览器上的“login”数保持为0)。 要解决这个问题,你需要build立一个credentialsProvider,并执行“credentialsProvider.getIdentityId”。 之后,login将显示正面,您可以根据您的身份validationangular色从AWS获取服务。

如果您正尝试为您的移动应用程序同时进行身份validation和未validation访问,则需要创buildAWSAnonymousCredentialsProvider(在单独的服务configuration中)。 然后,在注销时,使用self.credentialsProvider?.invalidateCachedTemporaryCredentials()和self.credentialsProvider?.clearCredentials(),并使用匿名服务configuration再次执行getidentityid,您将获得一个匿名ID。 (注意:我发现,如果您在凭证提供程序上清除密钥链,那么每次用户注销时都会以新ID开头,这可能会使您的免费50,000个ID快速烧毁。

更新3:

在Swift中为IOS用户池添加了一个github示例应用程序。

https://github.com/BruceBuckland/signin

更新2:

我终于让AWS User Pools在Swift中正常工作

我的问题是,每次身份validation开始发生这是由一个不同的viewcontroller(我的错误)authentication失败造成的。 我结束了一堆他们运行等待完成回报,从来没有来的API是“沉默”(显示没有错误)。 API没有注意到它是多次启动的(每次使用不同的viewController),所以它默默地让反复login。 原始文章中没有足够的代码来查看是否有相同的问题。

您必须小心,AWS示例代码(在Objective-C中)有两个导航控制器,代码将重新使用它们。 我不喜欢示例应用程序闪烁login视图控制器的身份validation委托之前,我正试图改善,在迅速版本,这导致了我的问题。

AWS User Pools API已设置为可以像这样工作的故事板或应用程序结构:

1)您的应用程序假设它已经login,然后触发委托触发身份validation和login屏幕,如果不是。

2)在原始的login视图控制器pool.currentUser()是不足以得到身份validation去,API只会触发委托时,你做更多(在我的情况user.getDetails())。

3)authentication通过didCompletePasswordAuthenticationStepWithError完成。 如果您得到身份validation(或其他)错误,并且您成功完成身份validation,则会调用此委托方法。 在成功validation的情况下,NSError是零,所以它应该被声明为NSError? 在委托(这导致一个警告)。 该API是testing版,他们可能会解决这个问题。

4)另一个小小的“疑难杂症”,对您来说很明显,它引起了我的兴趣,当您在控制台中定义用户池时,您指定了允许的应用程序,并且这些应用程序中的每个应用程序都具有用于客户端IDstring的不同string。 (我只是插入相同的东西到这个例子中)工作不好(但不报错)。 API在报告部门需要一些工作。 它在工作的时候是非常好的,但是如果你把错误的客户端string传给它,它就什么都不会说。 也似乎没有说,如果你(像我一样)从不同的视图控制器调用API。 这只是从不同的视图控制器获取每个新的身份validation请求,并且什么也没说。

无论如何,它现在的作品。 我希望这有助于解决您的问题。

更新:

我终于getPasswordAuthenticationDetails执行。

事实certificate,它不会得到执行,直到当前用户的user.getDetails(即使没有当前用户)。

所以

let user = appDelegate.pool!.currentUser() let details = user!.getDetails()

将导致在第二行执行getPasswordAuthenticationDetailscallback。

看起来AWS UserPool的概念是我们编写一个假设我们有login用户的应用程序。 我们从该用户那里获取详细信息(例如,在初始视图控制器中),如果我们没有用户,代表将被启动。

IOS上用户池的AWS文档缺less一些重要的概念页面。 这些页面包含在(另外并行的)Android文档中。 我承认,我仍然在努力(天),让用户池快速工作,但阅读“主要类”和“重要概念”部分Android文档澄清了我很多。 我不明白为什么它从IOS文件中省略。

只需将我的2美分添加到使用Objective-c的人员和Amazon提供的示例应用程序CognitoYourUserPoolsSample即可 。 @布鲁斯0已经用他迅速的解决scheme涵盖了一切。 但是,如果遇到这种问题, getPasswordAuthenticationDetails在您login时没有被调用是因为您根本没有调用[self.user getDetails] 。 确实 – getDetails触发getPasswordAuthenticationDetails。 如果您在AWS示例应用程序中仔细观察,则会在调用UserDetailTableViewController的 viewDidLoad(它是第一个加载的控制器)的应用程序时启动应用程序。 如果用户没有login,getDetails响应以某种方式触发SignInViewController。 这是我将在下面解释。 这就像一个“myHomeViewController”types,你想显示用户相关的信息。 否则,您要默认显示login/注册屏幕。

  • 一般来说,连接并初始化AppDelegate中的Cognito用户池( didFinishLaunchingWithOptions )就像在示例应用程序中一样。 确保添加AWSCognitoIdentityInteractiveAuthenticationDelegate并实现startPasswordAuthentication ,您将在其中启动您的loginViewController。 让AppDelegate处理WHAT_TO_DO_IF_USER_NOT_SIGNED_IN(例如,将SignInViewController放在最前面),然后将焦点放在应用程序某处的WHEN_DOES_THE_USER_NEEDS_TO_SIGNIN上。

  • 当你需要用户特定的数据,然后告诉应用程序,是时候检查用户是否login(self.user getDetails)。 同样,如果用户没有login,AppDelegate知道该怎么做。 它过度规则的应用程序,并显示在所有的顶部的login视图。 所以,一开始可能是正确的(例如Facebook,Twitter等)或其他地方(例如Ebay等)。 只需在viewDidLoad的末尾调用[self.user getDetails]即可 。 这将阻止当前的ViewController在authentication步骤(login/注册)之前显示,或者如果用户已经login,只需加载当前的ViewController。

在您的应用中使用AWS用户池function时,请按照以下步骤操作:

  1. 在任何YourViewController中找出你需要用户特定的数据的地方
  2. 在相关的ViewDidLoad中,调用[self.user getDetails]
  3. 如果用户已经login,则使用self.user getDetails的completionHandler显示用户特定的数据。
  4. 如果没有,那么startPasswordAuthentication会在AppDelegate中自动调用,这会在显示YourViewController之前调出ViewController,因为您需要用户特定的数据
  5. 用户login或注册
  6. 解散login/注册ViewController,那么你回到YourViewController,现在可以加载一些用户特定的数据。

AWS示例应用程序不是简单的,而是非常简单。

谢谢艾略特。 我正在尝试写几天这个代码的快速版本。

我尝试使用下面的代码显式signIn。

 @IBAction func signInButtonPressed(sender: AnyObject) { var emailTextField = "username" var passwordTextField = "password" let serviceConfiguration = AWSServiceConfiguration(region: AWSRegionType.USEast1, credentialsProvider: nil) AWSServiceManager.defaultServiceManager().defaultServiceConfiguration = serviceConfiguration let configurationUserPool = AWSCognitoIdentityUserPoolConfiguration.init(clientId: "####", clientSecret: "#####", poolId: "#####") AWSCognitoIdentityUserPool.registerCognitoIdentityUserPoolWithConfiguration(serviceConfiguration, userPoolConfiguration: configurationUserPool, forKey: "TestUserPool") let userPool = AWSCognitoIdentityUserPool(forKey: "TestUserPool") let user = userPool.getUser(emailTextField) user.getSession(emailTextField, password: passwordTextField, validationData: nil, scopes: nil).continueWithExecutor(AWSExecutor.mainThreadExecutor(), withBlock: { (task:AWSTask!) -> AnyObject! in if task.error == nil { print("No Error") print(task.result) } else { print("Some Error") print(task.error) } return nil }) } 

当我提供正确的凭据时,它将转到错误块。 每次运行代码时,validation码都会发送到我的手机,尽pipe我已经在注册过程中validation了我的用户。 响应体是

 Response body: {"AuthState":"H4sIAAAAAAAAAAXB3ZJzMAAA0EeqBDvTnfkulhVCpRXyI3dNmI8KzU7bLZ5+z+m3HBjINz2jp6rxB174rmT+agWweHyPLVydEqFXi2o8j9gjTT6XcH1qeA+vWWQVbAMDW6gXvhEYgHOMH3gmg06pNTP61pBaNvO1E3zvEPFaSS2+3ccuQ6qUVvXcYjqBQKFoKvfoJHgLDKJx3VhlkKsIUGs7qbhH6qXZ3a9kl+v0uPEEOWqR0/7gk4T8iiYPm0XBXt59LivPwAGUmSr1RAfDqSz8COhkZcQLFdsev3oGVw3oWTRRXIHuRkTuqYS6/juHBIYRgzTsZ1crqHB5I5OZ2JvaMmB2aKqpS2qYizMqg5KjgqI24DtNGLfXenGu8/+/zU5ZnZlVCXTRNwtKxgXP2k0LJK9T58TCnxxRJtLnQ7AAFD4lZpnWk+dY4fGBCFqZlP4YyUGfqVQ3rW/i/PgJPnd8WN8fw/Hr5D0OChfhfCleb290yaV/AXf4itllINJONfv3B7RgGQzfAQAA","CodeDeliveryDetails": {"DeliveryMedium":"SMS","Destination":"+*******8869"}} Some Error Optional(Error Domain=com.amazonaws.AWSCognitoIdentityProviderErrorDomain Code=-1000 "startMultiFactorAuthentication not implemented by authentication delegate" UserInfo={NSLocalizedDescription=startMultiFactorAuthentication not implemented by authentication delegate}) 

当我提供了错误的密码响应正文

 Response body: {"__type":"NotAuthorizedException","message":"Incorrect username or password."} Some Error Optional(Error Domain=com.amazonaws.AWSCognitoIdentityProviderErrorDomain Code=12 "(null)" UserInfo={__type=NotAuthorizedException, message=Incorrect username or password.}) 

你能build议我在这里做错了吗?

我也遵循了原始海报问题中提到的相同步骤,但是应用程序从不在启动时切换到login屏幕。 我已经validation了切换视图的代码是正确的,直接放在AppDelegate.application(….)方法中。 看来,startPasswordAuthentication(…)删除方法永远不会被调用。 有人可以通过AWSCognitoIdentityInteractiveAuthenticationDelegate协议发布指向示例应用程序的链接吗?

似乎在代理中没有实现“startMultiFactorAuthentication”方法,这就是为什么不正确的密码被检测到,但是当给定纠正的密码时,它被升级到MFA,但是在委托中没有findMFAfunction,因此login失败。

对我来说,我一直得到这个错误,因为我有validation,但我没有在尝试login之前validation用户。 一旦validation一切正常。

我遵循同样的例子,并处理同样的问题,我还没有完全设法解决它,但我强烈怀疑这个问题有关该函数永远不会执行:

 func getPasswordAuthenticationDetails(authenticationInput: AWSCognitoIdentityPasswordAuthenticationInput, passwordAuthenticationCompletionSource: AWSTaskCompletionSource) { self.passwordAuthenticationCompletion = passwordAuthenticationCompletionSource dispatch_async(dispatch_get_main_queue(), {() -> Void in if self.usernameText == nil{ self.usernameText = authenticationInput.lastKnownUsername } }) } 

我已经尝试在这个方法中放置断点和打印语句,它似乎从未被激活。 我build议在你的代码中做同样的事情,因为这听起来像你的问题是相同的我的。 我看了这个例子,无法find手动调用方法的地方。 我注意到,在你的代码中你初始化passwordAuthenticationCompletion的值是这样的:

 var passwordAuthenticationCompletion = AWSTaskCompletionSource() 

getPasswordAuthenticationDetails()似乎应该在login方法中的这一行使用相关的值之前被调用: self.passwordAuthenticationCompletion.setResult(AWSCognitoIdentityPasswordAuthenticationDetails(username: emailTextField.text, password: passwordTextField.text))

我已经停留了一段时间,试图得到比这更进一步,但我仍然认为这是实现用户注册/login的正确路线。 这个例子中的一些代码可能不会干净地转换成Swift,所以一些重要的函数不会被触发。 如果我确认解决scheme,我会继续研究并更新我的答案。

我遵循了前面提到的使用Swift的相同步骤,我发现didCompletePasswordAuthenticationStepWithError从来没有被调用过,尽pipeLogInViewController扩展了AWSCognitoIdentityPasswordAuthentication

另外,即使委托也实现了AWSCognitoIdentityInteractiveAuthenticationDelegate但在委托中不调用startPasswordAuthentication()

我想知道是否这是Swift实现的问题,因为Amazon提供的示例Objective-C具有所有这些工作。

我觉得在这方面有问题:

Object-c – >

 self.passwordAuthenticationCompletion.result = [[AWSCognitoIdentityPasswordAuthenticationDetails alloc] initWithUsername: username password:password]; 

Swift – > self.passwordAuthenticationCompletion.setResult(AWSCognitoIdentityPasswordAuthenticationDetails.init(username: username, password: password))

并且因为在前面的语句中有错误,所以“didCompletePasswordAuthenticationStepWithError”方法不会触发。

我尝试了很多东西没有运气:( – 我试图在Swift中实现每一件事(不工作) – 我试图将Object-C文件添加到我的基于Swift的项目(不工作)

所以,我想我会用原始样本作为我的项目的首发。

更新:

我在Swift中实现SignInViewController&MFAController,并在基于Object-C的项目中导入这些Swift文件。 它工作正常! 所以,现在我确信在基于Swift的项目中实现“AWSCognitoIdentityPasswordAuthentication”&&“AWSCognitoIdentityMultiFactorAuthentication”协议时会出现问题或错误。 我find的唯一解决scheme是与基于Object-c的项目。

这就是我在Swift中如何使用一个ViewController而不用在AppDelegate中设置任何东西:

 class LoginViewController: UIViewController, AWSCognitoIdentityInteractiveAuthenticationDelegate, AWSCognitoIdentityPasswordAuthentication { var passwordAuthenticationCompletion = AWSTaskCompletionSource<AWSCognitoIdentityPasswordAuthenticationDetails>() var pool = AWSCognitoIdentityUserPool.init(forKey: "UserPool") override func viewDidLoad() { super.viewDidLoad() //setup service configuration let serviceConfiguration = AWSServiceConfiguration.init(region: .USEast1, credentialsProvider: nil) //create and config a pool let configuration = AWSCognitoIdentityUserPoolConfiguration.init(clientId: "YourClientId", clientSecret: "YourClientId", poolId: "YourPoolId") AWSCognitoIdentityUserPool.register(with: serviceConfiguration, userPoolConfiguration: configuration, forKey: "UserPool") pool = AWSCognitoIdentityUserPool.init(forKey: "UserPool") pool.delegate = self } @IBAction func logInButtonPressed(sender: UIButton) { pool.getUser().getDetails() } func startPasswordAuthentication() -> AWSCognitoIdentityPasswordAuthentication { return self } func getDetails(_ authenticationInput: AWSCognitoIdentityPasswordAuthenticationInput, passwordAuthenticationCompletionSource: AWSTaskCompletionSource<AWSCognitoIdentityPasswordAuthenticationDetails>) { self.passwordAuthenticationCompletion = passwordAuthenticationCompletionSource let result = AWSCognitoIdentityPasswordAuthenticationDetails.init(username: "username", password: "password") } }