iOS (Swift) 应用程序中的 AWS Cognito 用户池 [英] AWS Cognito User Pools in iOS (Swift) app

查看:58
本文介绍了iOS (Swift) 应用程序中的 AWS Cognito 用户池的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在我的 iOS (Swift) 应用程序中实施新的 AWS Cognito 用户池,但我正在努力使登录过程正常工作.我基本上是在尝试遵循可用的示例 此处.

I'm trying to implement the new AWS Cognito User Pools in my iOS (Swift) app, but I'm struggling to get the sign in process to work. I am essentially trying to follow the example available here.

这是我目前所拥有的:

应用程序委托:

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
    }
}

登录视图控制器:

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))
    }
}

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

Nothing seems to happen when I hit the log in button, although if I hit it again I get an NSInternalInconsistencyException (which I believe is because the AWSTask result has already been set).

对此的任何帮助将不胜感激.我使用的是适用于 iOS 版本 2.4.1 的 AWS 开发工具包.

Any help with this would be appreciated. I am using the AWS SDK for iOS version 2.4.1.

更新:

不是我最初问题的解决方案,但我已经能够通过使用显式登录方法而不是委托方法来使用户池工作(请参阅此 页面 了解详情).这是我的 SignInViewController 中的代码:

Not a solution to my original problem, but I've been able to get User Pools working by using the explicit sign in method rather than the delegate method (see this page for details). Here is the code from my 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 位于不同的区域),我使用用户池创建了一个新的凭据提供程序:

Then, to consume an AWS service (which in my case is located in a different region to Cognito) I have created a new Credentials Provider using the User Pool:

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 中找不到有效的‘AWSDefaultRegionType’、‘AWSCognitoRegionType’和‘AWSCognitoIdentityPoolId’值.".这似乎与我用来跟踪崩溃的 Fabric 相关.我已经通过更改 AppDelegate 中的这一行解决了这个问题:

One additional issue is that I was seeing this error each time I launched the app: "Could not find valid 'AWSDefaultRegionType', 'AWSCognitoRegionType', and 'AWSCognitoIdentityPoolId' values in info.plist.". This seems to be related to Fabric, which I am using to track crashes. I've solved this by changing this line in the AppDelegate:

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

为此:

Fabric.with([Crashlytics.self])

我希望这对其他人有所帮助.

I hope this helps someone else.

推荐答案

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

Update 6: (and really final this time )

值得一提的是,(最后)AWS 已经让 AWS Mobile Hub 构建了一个非常好的演示应用程序,其中包括用户池作为登录提供者(谷歌和 Facebook 也是如此).该架构(在我看来)非常出色(他们将身份管理和从身份验证中获取凭据分开了)检查一下

It is worth mentioning that (finally) AWS has made the AWS Mobile Hub build a very nice demo app that INCLUDES User Pools as a SignInProvider (With Google and Facebook too). The architecture is (in my opinion) excellent (they have separated Identity Management and getting credentials from Authentication) Check it out

更新 5:(和最终版)

Update 5: (and final)

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

There is a fairly complete example implementation, and some documentation of how it works in this other answer.

iOS - AWS MobileHub 登录开发者认证提供者

更新 4:

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

If you want to get access to AWS services, there are more steps needed

事实证明,这不会让您使用 Cognito 联合身份进行身份验证(身份浏览器上的登录"计数保持为 0).要解决此问题,您需要建立一个凭据提供程序并执行credentialsProvider.getIdentityId".之后登录将显示为肯定,您可以根据您的身份验证角色从 AWS 获取服务.

It turns out that this does not get you authenticated with Cognito Federated Identities (the "logins" count on the identity browser remains at 0). To fix this you need to establish a credentialsProvider and do "credentialsProvider.getIdentityId". After that logins will show positive, and you can get services from AWS based upon your authenticated role.

如果您尝试对您的移动应用程序进行 Authenticated 和 UnAuthenticated 访问,那么您需要创建一个 AWSAnonymousCredentialsProvider(在单独的服务配置中).然后您在注销时 self.credentialsProvider?.invalidateCachedTemporaryCredentials() 和 self.credentialsProvider?.clearCredentials() 并使用匿名服务配置再次执行 getidentityid ,您将获得一个匿名 ID.(注意:我发现如果您在凭据提供程序上清除钥匙串,每次用户注销时它都会以新 ID 开头,这可能会很快消耗掉您的 50,000 个免费 ID.)

If you are trying to do both Authenticated and UnAuthenticated access for your mobile app, then you need to create an AWSAnonymousCredentialsProvider (in a separate service configuration). Then you self.credentialsProvider?.invalidateCachedTemporaryCredentials() and self.credentialsProvider?.clearCredentials() when logging out and do the getidentityid again with the anonymous service configuration and you will get an anonymous id. (Note: I found it seemed like if you clearkeychain on the credentialsProvider it starts with a new id each time a user logs out, which could burn up your free 50,000 ids pretty quick. )

更新 3:

在 Swift 中上传了适用于 IOS 的 AWS 用户池的 github 示例应用程序.

Uploaded a github sample app for AWS User Pools for IOS in Swift.

https://github.com/BruceBuckland/signin

更新 2:

我终于让 AWS 用户池在 Swift 中正常工作

I finally got AWS User Pools to work correctly in Swift

我的问题是每次身份验证开始时都是由不同视图控制器中的身份验证失败引起的(我的错误).我最终让他们中的一群人在等待完成返回,而这些返回从未出现,并且 API 是静默的"(没有显示任何错误).API 不会注意到它被多次启动(每次由不同的 viewController 启动),因此它会默默地一遍又一遍地登录.原始帖子中没有足够的代码来查看您是否遇到同样的问题.

My problem was that each time the authentication start happened it was caused by an authentication failure in a different viewcontroller (my error). I ended up with a bunch of them running waiting for completion returns which never came and the API was "silent" (showed no errors). The API does not notice that it is being initiated multiple times ( by a different viewController each time) so it silently lets log in over and over. There is not enough of your code in the original post to see if you are having that same issue.

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

You have to be careful, the AWS sample code (in Objective-C) has two navigation controllers, and the code re-uses them. I don't like the way the sample app flashes the logged in view controller before the authentication delegate gets going and I was trying to improve that in the swift version and that caused my problem.

AWS 用户池 API 设置为使用故事板或应用程序结构,其工作方式如下:

AWS User Pools API is set up to work with a storyboard or app structure that works like this:

1) 您的应用假定它已登录,然后触发代理,如果未登录,则会触发身份验证和登录屏幕.

1) Your app ASSUMES it is logged in, and then triggers the delegate which triggers authentication and the login screens if it is not.

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

2) In original logged in view controller pool.currentUser() is NOT enough to get the authentication going, API will only trigger the delegate when you do more (in my case user.getDetails()).

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

3) The authentication is completed through the didCompletePasswordAuthenticationStepWithError. This delegate method is called if you get an authentication (or other) error AND if you SUCCESSFULLY authenticate. In the case of successful authentication the NSError is nil, so it should be declared as NSError? in the delegate (this causes a warning). The API is beta, they will probably fix this.

4) 另一个小问题",对你来说可能很明显,它抓住了我,当你在控制台中定义你的用户池时,你指定了允许的应用程序,这些应用程序中的每一个都有不同的客户端 ID 字符串字符串.(我只是在示例中插入了相同的东西)效果不佳(但不报告错误).API 需要报告部门的一些工作.它在工作时非常冗长,但如果您传递错误的客户端字符串,则什么也不说.此外,如果您(像我一样)从不同的视图控制器调用 API,它似乎什么也没说.它只是从不同的视图控制器接收每个新的身份验证请求,然后什么也不说.

4) One other little "gotcha", it may be obvious to you, it caught me, when you define your User Pool in the console you specify allowed apps, and each of these apps HAS DIFFERENT STRINGS for Client ID strings. ( I was just plugging the same thing into the example) which works badly (but does not report errors). The API needs some work in the reporting department. It is very Verbose when it is working, but says nothing if you pass it the wrong Client Strings. Also it seems to say nothing if you (like I did) call the API from different viewcontrollers. It was just taking each new authentication request from a different viewcontroller and saying nothing.

无论如何,它现在可以工作了.我希望这有助于解决您的问题.

Anyway, it works now. I hope this helps resolve your issue.

更新:

我终于得到了 getPasswordAuthenticationDetails 来执行.

I finally got getPasswordAuthenticationDetails to execute.

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

It turns out it does not get executed until user.getDetails for the current user (even if there is no current user).

所以

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

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

will result in the getPasswordAuthenticationDetails callback getting executed on the second line.

AWS UserPool 的概念似乎是我们编写的应用程序假设我们有一个登录用户.我们从该用户那里获取详细信息(例如在初始视图控制器中),如果我们没有用户,则委托将被启动.

It seems the AWS UserPool concept is that we write an app that assumes we have a logged in user. We get details from that user (for instance in the initial view controller) and the delegate gets kicked off if we don't have a user.

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

The AWS documentation for User Pools on IOS is missing some important concept pages. Those pages ARE included in the (otherwise parallel) Android documentation. I admit that I am still struggling (days now) with getting User Pools to work in swift, but reading the "Main Classes" and "Key Concepts" Parts of the Android documentation clarified a lot for me. I can't see why it was omitted from the IOS doc.

这篇关于iOS (Swift) 应用程序中的 AWS Cognito 用户池的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆