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

Ell*_*iot 13 amazon-web-services ios amazon-cognito

我正在尝试在我的iOS(Swift)应用程序中实现新的AWS Cognito用户池,但我很难让登录过程工作.我基本上试图遵循这里提供的示例.

这是我到目前为止:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

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))
    }
}
Run Code Online (Sandbox Code Playgroud)

当我点击登录按钮时似乎没有发生任何事情,但是如果我再次点击它,我会得到一个NSInternalInconsistencyException(我相信这是因为已经设置了AWSTask结果).

任何帮助都将不胜感激.我正在使用AWS SDK for iOS 2.4.1版.

更新:

不是我原来问题的解决方案,但我已经能够通过使用显式登录方法而不是委托方法来使用户池工作(有关详细信息,请参阅此页面).这是我的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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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")
Run Code Online (Sandbox Code Playgroud)

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

Fabric.with([AWSCognito.self, Crashlytics.self])
Run Code Online (Sandbox Code Playgroud)

对此:

Fabric.with([Crashlytics.self])
Run Code Online (Sandbox Code Playgroud)

我希望这有助于其他人.

Bru*_*ce0 16

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

值得一提的是(最终)AWS已经使AWS Mobile Hub构建了一个非常好的演示应用程序,其中包括用户池作为SignInProvider(也包括Google和Facebook).该体系结构(在我看来)很出色(它们将身份管理分开并从身份验证中获取凭据)检查出来

更新5 :(和最后的)

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

iOS - AWS MobileHub使用开发人员验证的提供商登录

更新4:

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

事实证明,这不会让您通过Cognito Federated Identities进行身份验证(身份浏览器上的"登录"计数保持为0).要解决此问题,您需要建立credentialsProvider并执行"credentialsProvider.getIdentityId".之后,登录将显示为正,您可以根据经过身份验证的角色从AWS获取服务.

如果您尝试对移动应用程序执行Authenticated和UnAuthenticated访问,则需要创建AWSAnonymousCredentialsProvider(在单独的服务配置中).那么你self.credentialsProvider?.invalidateCachedTemporaryCredentials()和self.credentialsProvider?.clearCredentials()在注销时再次使用匿名服务配置执行getidentityid,你将得到一个匿名id.(注意:我发现,如果你在凭证提供者上使用clearkeychain,它会在每次用户注销时以新ID开头,这可能会很快烧掉你的免费50,000 ID.)

更新3:

在Swift中为IOS的AWS用户池上传了一个github示例应用程序.

https://github.com/BruceBuckland/signin

更新2:

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

我的问题是,每次身份验证开始发生时,都是由于另一个viewcontroller中的身份验证失败(我的错误)造成的.我最终得到了一大堆正在运行的等待完成返回,这些返回从未到来,而且API是"无声的"(没有显示错误).API没有注意到它被多次启动(每次都由不同的viewController),因此它可以无声地一次又一次地登录.原始帖子中没有足够的代码来查看您是否遇到同样的问题.

您必须要小心,AWS示例代码(在Objective-C中)有两个导航控制器,代码重新使用它们.我不喜欢示例应用程序在身份验证委托开始之前闪烁登录视图控制器的方式,我试图在swift版本中改进它并导致我的问题.

AWS User Pools API设置为使用如下所示的故事板或应用程序结构:

1)您的应用程序确定它已登录,然后触发触发身份验证的委托,如果不是,则触发登录屏幕.

2)在原始登录视图控制器pool.currentUser()不足以进行身份​​验证,API只会在您执行更多操作时触发委托(在我的情况下为user.getDetails()).

3)通过didCompletePasswordAuthenticationStepWithError完成认证.如果您获得身份验证(或其他)错误并且如果您已成功进行身份验证,则会调用此委托方法.在成功验证的情况下,NSError为nil,因此应将其声明为NSError?在委托中(这会导致警告).API是测试版,他们可能会解决这个问题.

4)另一个小"陷阱",它可能是显而易见的,它抓住了我,当您在控制台中定义用户池时指定允许的应用程序,并且每个应用程序都有不同的客户端ID字符串.(我只是将相同的东西插入到示例中),这很糟糕(但不报告错误).API需要报告部门的一些工作.它工作时非常详细,但如果你传递了错误的客户端字符串则没有说什么.如果你(像我一样)从不同的viewcontrollers调用API,它似乎也没有说什么.它只是从不同的viewcontroller中获取每个新的身份验证请求并且什么都不说.

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

更新:

我终于得到了getPasswordAuthenticationDetails来执行.

事实证明,直到当前用户的user.getDetails才会执行它(即使没有当前用户).

所以

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

将导致getPasswordAuthenticationDetails回调在第二行执行.

AWS UserPool概念似乎是我们编写了一个假定我们已登录用户的应用程序.我们从该用户那里获得详细信息(例如在初始视图控制器中),如果我们没有用户,代理就会被启动.

IOS上用户池的AWS文档缺少一些重要的概念页面.这些页面包含在(否则是并行的)Android文档中.我承认我仍然在努力(现在几天)让用户池在swift中工作,但阅读"主要类"和"关键概念"Android文档的部分内容为我澄清了很多.我不明白为什么它从IOS文档中省略了.


Ed.*_*Ed. 8

只需为使用Objective-c的人和亚马逊提供的示例应用CognitoYourUserPoolsSample添加2美分.@ Bruce0已经用他的快速解决方案涵盖了一切.但是如果你遇到这个问题,当你点击登录时没有调用getPasswordAuthenticationDetails是因为你根本没有调用[self.user getDetails].确实 - getDetails触发getPasswordAuthenticationDetails.如果您仔细观察AWS示例应用程序,他们会在UserDetailTableViewController的 viewDidLoad中启动应用程序时调用它,这是第一个加载的控制器.如果用户未登录,那么getDetails响应会以某种方式触发SignInViewController.这就是我将在下面解释的内容.它就像一个"myHomeViewController"类型,你想要显示用户相关的信息.否则,您希望默认显示登录/注册屏幕.

  • 作为一般经验法则,在AppDelegate(didFinishLaunchingWithOptions)中连接并初始化Cognito用户池,就像在示例应用程序中一样.确保添加AWSCognitoIdentityInteractiveAuthenticationDelegate并实现startPasswordAuthentication,您将在其中启动登录ViewController.让AppDelegate处理WHAT_TO_DO_IF_USER_NOT_SIGNED_IN(例如将SignInViewController置于顶部),然后专注于应用中某处的WHEN_DOES_THE_USER_NEEDS_TO_SIGNIN.

  • 当您需要特定于用户的数据时,请告知应用程序是时候检查用户是否已登录(self.user getDetails).同样,如果用户未登录,则AppDelegate知道该怎么做.它过度管理应用程序并在所有内容上显示登录视图.因此,它可能在开始时(例如Facebook,Twitter等)或其他地方(例如Ebay等).只需在viewDidLoad的末尾调用[self.user getDetails]即可.这将阻止当前ViewController在身份验证步骤(登录/注册)之前显示,或者只是在用户已登录时加载当前的ViewController.

在应用中使用AWS User Pool功能时,请按照以下步骤操作:

  1. 在任何YourViewControllers中找出您需要用户特定数据的位置
  2. 在相关的ViewDidLoad中,调用[self.user getDetails]
  3. 如果用户已登录,则使用self.user getDetails的completionHandler显示用户特定数据.
  4. 如果没有,则在AppDelegate中自动调用startPasswordAuthentication,这会在显示YourViewController之前调出登录ViewController,因为您需要用户特定的数据
  5. 用户登录或注册
  6. 关闭登录/注册ViewController,然后返回到YourViewController,现在可以加载一些用户特定的数据.

AWS示例应用程序不是直截了当的,但它非常简单.