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

AWS Cognito User Pools in iOS (Swift) app

我正在尝试在我的 iOS (Swift) 应用程序中实施新的 AWS Cognito 用户池,但我很难让登录过程正常工作。我本质上是在尝试遵循可用的示例 here.

这是我目前拥有的:

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

登录视图控制器:

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 结果已经设置)。

如有任何帮助,我们将不胜感激。我正在使用 iOS 版本 2.4.1.

的 AWS SDK

更新:

不是我原来问题的解决方案,但我已经能够通过使用显式登录方法而不是委托方法来让用户池工作(有关详细信息,请参阅此 page)。这是我的 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 不同的区域),我使用用户池创建了一个新的 Credentials Provider:

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

另一个问题是我每次启动应用程序时都会看到此错误:"Could not find valid 'AWSDefaultRegionType', 'AWSCognitoRegionType', and 'AWSCognitoIdentityPoolId' values in info.plist."。这似乎与我用来跟踪崩溃的 Fabric 有关。我通过更改 AppDelegate 中的这一行解决了这个问题:

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

对此:

Fabric.with([Crashlytics.self])

我希望这对其他人有帮助。

谢谢埃利奥特。这几天我正在尝试编写此代码的 swift 版本。

我尝试使用以下代码使用显式登录。

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

当我提供正确的凭据时,它会进入错误块。每次我 运行 验证码都会发送到我的手机,尽管我已经在注册过程中验证了我的用户。响应正文是

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

你能指出我做错了什么吗?

似乎方法 "startMultiFactorAuthentication" 没有在委托中实现,这就是为什么检测到不正确的密码,但是当给出更正的密码时,它升级到 MFA,但是找不到启动 MFA 函数在委托中,因此登录失败。

对我来说,我一直收到这个错误,因为我已经进行了验证,但我在尝试登录之前没有验证用户。一旦验证一切正常。

我正在按照同样的例子处理同样的问题,但我还没有完全解决它,但我强烈怀疑这个问题与这个从未执行过的函数有关:

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

我试过在此方法中放置断点和打印语句,但它似乎从未被激活。我建议在您的代码中执行相同的操作,因为听起来您的问题与我的相同。我查看了示例,但无法找到手动调用该方法的地方。我注意到在您的代码中,您像这样初始化 passwordAuthenticationCompletion 的值:

var passwordAuthenticationCompletion = AWSTaskCompletionSource()

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

我已经被困了一段时间,现在试图做更多的事情,但我仍然认为这是实现用户 registration/login 的正确途径。示例中的一些代码可能没有完全转换为 Swift,因此一些重要的功能没有被触发。如果我确认解决方案,我会继续调查并更新我的答案。

我也遵循了原始 post 提问者中提到的相同步骤,但应用程序在启动时从未切换到登录屏幕。我已经通过直接放置在 AppDelegate.application(....) 方法中验证了切换视图的代码是正确的。似乎从未调用过 startPasswordAuthentication(...) delete 方法。有人可以 post link 使用 AWSCognitoIdentityInteractiveAuthenticationDelegate 协议切换到登录屏幕的示例应用程序吗?

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

值得一提的是,(最终)AWS 使 AWS Mobile Hub 构建了一个非常好的演示应用程序,其中包括用户池作为 SignInProvider(还有 Google 和 Facebook)。该体系结构(在我看来)非常好(他们将身份管理和从身份验证中获取凭证分开) 看看

更新 5:(也是最终的)

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

更新 4:

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

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

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

更新 3:

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

https://github.com/BruceBuckland/signin

更新 2:

我终于让 AWS 用户池在 Swift

中正常工作

我的问题是每次启动身份验证都是由不同 viewcontroller 中的身份验证失败引起的(我的错误)。我最终得到了一堆 运行 等待完成 returns 但从未出现, API 是 "silent" (没有显示错误)。 API 没有注意到它被多次启动(每次由不同的 viewController 启动)所以它默默地让登录一遍又一遍。原始 post 中的代码不足,无法查看您是否遇到同样的问题。

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

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

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

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

3) 通过didCompletePasswordAuthenticationStepWithError完成鉴权。如果您收到身份验证(或其他)错误并且您成功通过身份验证,则会调用此委托方法。如果身份验证成功,NSError 为 nil,那么它应该声明为 NSError 吗?在委托中(这会导致警告)。 API 是测试版,他们可能会解决这个问题。

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

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

更新:

我终于得到了要执行的 getPasswordAuthenticationDetails。

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

所以

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

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

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

IOS 上用户池的 AWS 文档缺少一些重要的概念页面。这些页面包含在(否则平行)Android 文档中。我承认我仍在努力(现在几天)让用户池在 swift 中工作,但阅读 "Main Classes" 和 "Key Concepts" 部分 Android 文档澄清了很多为了我。我不明白为什么 IOS 文档中省略了它。

我使用 Swift 执行了前面提到的相同步骤,我注意到 didCompletePasswordAuthenticationStepWithError 从未被调用,尽管 LogInViewController 扩展了 AWSCognitoIdentityPasswordAuthentication

此外,即使委托也实现了 AWSCognitoIdentityInteractiveAuthenticationDelegate.

,也不会在委托中调用 startPasswordAuthentication()

我想知道这是否是 Swift 实施的问题,因为亚马逊提供的示例 Objective-C 具有所有这些功能。

我认为这里有问题:

对象-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,并将这些 Swift 文件导入到基于 Object-C 的项目中。它工作正常! 所以,现在我确定当我们尝试在基于 Swift 的项目中实施 "AWSCognitoIdentityPasswordAuthentication" && "AWSCognitoIdentityMultiFactorAuthentication" 协议时存在问题或错误。 我找到的唯一解决方案是使用基于 Object-c 的项目。

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

  • 根据一般经验,在您的 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 在身份验证步骤 (sign-in/sign-up) 之前显示,或者如果用户已经登录,则只需加载当前 ViewController。

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

  1. 在您的任何ViewController
  2. 中找出您需要用户特定数据的地方
  3. 在相关的ViewDidLoad中,调用[self.user getDetails]
  4. 如果用户已经登录,则使用 self.user getDetails 的 completionHandler 显示用户特定数据。
  5. 如果不是,则在 AppDelegate 中自动调用 startPasswordAuthentication,这会在显示 YourViewController 之前显示登录 ViewController,因为您需要用户特定数据
  6. 用户登录或注册
  7. dissmiss sign-in/sign-up ViewController 然后你回到你的ViewController,现在可以加载一些用户特定的数据。

AWS 示例应用程序并不直接,但它非常简单。

这就是我在 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")
        }

}

在 iOS 中集成 Cognito 的人很棘手,尤其是当您是像我这样的新手时,好吧,上面的答案是正确的,但是如果您仍在努力或陷入实施部分而不是这个答案可能有帮助。

我假设包含 MFA 或简单登录的场景。

这就是我的 CognitoUserManagerClass 的样子。

 class CognitoUserManager: NSObject {

static var shared = CognitoUserManager()

var pool : AWSCognitoIdentityUserPool?

func awsConfig(){
    let configuration = AWSServiceConfiguration(region: Constants.AWSRegionName, credentialsProvider: nil)
    let poolConfiguration = AWSCognitoIdentityUserPoolConfiguration(clientId: Constants.AWSClientId,
                                                                    clientSecret: Constants.AWSClientSecret,
                                                                    poolId: Constants.AWSPoolId)
    AWSCognitoIdentityUserPool.register(with: configuration, userPoolConfiguration: poolConfiguration, forKey: "POOL_NAME")
}


func getPoolFromAWS(name: String){
    pool = AWSCognitoIdentityUserPool(forKey: name)
}

func clearDataAndSignout(){
     //This will clear all the data stored in Keychain or user session. 
    pool?.clearAll()
}

我更喜欢为所有与 Cognito 相关的配置使用单独的 class。

应用程序启动后。在 AppDelegate 的 didFinishLaunch 函数中调用它。

    CognitoUserManager.shared.awsConfig()
    CognitoUserManager.shared.getPoolFromAWS(name: "POOL_NAME")

现在,第一步是检查用户是否登录?

第 1 步。在 didFinishLaunch 之后,我调用了 InitialViewController,我决定根据 KeyChain 中存在的令牌将其发送到主屏幕或登录屏幕。

注意:当您在 iOS 中通过 Cognito 通过任何方法成功登录时,Cognito 将自行负责将令牌保存在钥匙串中。

 class InitialViewController: UIViewController {

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(true)

    CognitoUserManager.shared.pool!.currentUser()?.getSession().continueWith { task in
        if let error = task.error as NSError?{
            //Print ERROR
            self.login()
        }else{
            guard let accessToken = task.result?.accessToken?.tokenString else {
                self.login()
                return nil
            }
            self.home()
        }
        return nil
    }
  }
}

第 2 步。我在登录屏幕上获取了用户电子邮件和密码,并将其发送到 OTP(MFA) 屏幕并在 MFA 身份验证屏幕上调用 getSession(使用电子邮件和密码),以避免发送多个 OTP给用户。这是基于设计要求,你可以选择你的方式。

第 3 步。我的 MFA 身份验证屏幕我正在做所有与登录相关的事情看起来像这样。

 class OTPViewController : UIViewController {


var mfaCodeCompletionSource: AWSTaskCompletionSource<NSString>?
var user: AWSCognitoIdentityUser?

override func viewDidLoad() {
    super.viewDidLoad()

    CognitoUserManager.shared.pool?.delegate = self

    guard let email = emailText, let pass = passwordText else {return}
    user = CognitoUserManager.shared.pool?.getUser(email)
    //HERE IF YOU'RE USING SIMPLE USER POOL WITH MFA THEN YOU'LL BE DIRECTED TO HOME SCREEN AND getSession() will return tokens, but if you're using MFA then it will trigger and send OTP on your Mobile.
    user?.getSession(email, password: pass, validationData: nil).continueWith(block: { (task) -> Any? in
        if let error = task.error as NSError?{
            //PRINT ERROR
        }else{
            let accessToken = task.result?.accessToken?.tokenString
            print(accessToken)
            self.sendToHome()
        }

        return nil
    })  
}

//GETTING OTP FROM TEXT FIELD AND SETTING IT.
@IBAction func SubmitOTPButtonPressed(_ sender: Any) {
    guard let otp = securtiyCodeTextfield.text else {return}
    self.mfaCodeCompletionSource?.set(result: otp as NSString)
}

}

  extension OTPViewController : AWSCognitoIdentityInteractiveAuthenticationDelegate{

func startMultiFactorAuthentication() -> AWSCognitoIdentityMultiFactorAuthentication {
    //THIS WILL CALLED IF YOU'RE USING MFA.
    return self
}

func startNewPasswordRequired() -> AWSCognitoIdentityNewPasswordRequired {
    //Called for first time to change the temporary Password.
    return self
}

func startPasswordAuthentication() -> AWSCognitoIdentityPasswordAuthentication {
    return self
}

 }


  extension OTPViewController : AWSCognitoIdentityNewPasswordRequired, AWSCognitoIdentityPasswordAuthentication, AWSCognitoIdentityMultiFactorAuthentication{

func getCode(_ authenticationInput: AWSCognitoIdentityMultifactorAuthenticationInput, mfaCodeCompletionSource: AWSTaskCompletionSource<NSString>) {
    //MFA
    self.mfaCodeCompletionSource = mfaCodeCompletionSource
}

func didCompleteMultifactorAuthenticationStepWithError(_ error: Error?) {
    print(error.debugDescription)
}

func getDetails(_ authenticationInput: AWSCognitoIdentityPasswordAuthenticationInput, passwordAuthenticationCompletionSource: AWSTaskCompletionSource<AWSCognitoIdentityPasswordAuthenticationDetails>) {
}

func didCompleteStepWithError(_ error: Error?) {
    print(error.debugDescription)
}

func getNewPasswordDetails(_ newPasswordRequiredInput: AWSCognitoIdentityNewPasswordRequiredInput, newPasswordRequiredCompletionSource: AWSTaskCompletionSource<AWSCognitoIdentityNewPasswordRequiredDetails>) {
    //SEND TO SETNEWPASSWORD CONTROLLER TO SET NEW PASSWORD
}
func didCompleteNewPasswordStepWithError(_ error: Error?) {
    print(error.debugDescription)
}

}

如果您觉得此答案需要编辑,请继续。