AWS Cognito登录不起作用(Swift-iOS)

时间:2018-06-22 12:59:46

标签: ios swift amazon-web-services amazon-cognito aws-cognito

我已经将Cognito集成到我的xcode项目中。注册/密码更新功能正常运行。但是,我似乎无法使登录过程正常运行。我打开了日志,并收到以下错误

{"__type":"NotAuthorizedException","message":"Access Token has expired"}


Domain=com.amazonaws.AWSCognitoIdentityProviderErrorDomain Code=-1000 "Authentication delegate not set" UserInfo={NSLocalizedDescription=Authentication delegate not set}]

我还已经在 AppDelegate 脚本中实现了 AWSCognitoIdentityInteractiveAuthenticationDelegate 委托。

这是AppDelegate代码

class AppDelegate: UIResponder, UIApplicationDelegate {

    class func defaultUserPool() -> AWSCognitoIdentityUserPool {
        return AWSCognitoIdentityUserPool(forKey: userPoolID)
    }

    var window: UIWindow?
    var loginViewController: LoginVC?
    var navigationController: UINavigationController?
    var storyboard: UIStoryboard?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.

        // Warn user if configuration not updated
        if (CognitoIdentityUserPoolId == "us-east-1_TavWWBZtI") {
            let alertController = UIAlertController(title: "Invalid Configuration",
                                                    message: "Please configure user pool constants in Constants.swift file.",
                                                    preferredStyle: .alert)
            let okAction = UIAlertAction(title: "Ok", style: .default, handler: nil)
            alertController.addAction(okAction)
            self.window?.rootViewController!.present(alertController, animated: true, completion:  nil)
            //print("Please configure user pool constants in Constants.swift file.")
        }

        // setup logging
        AWSDDLog.sharedInstance.logLevel = .verbose
        AWSDDLog.add(AWSDDTTYLogger.sharedInstance)

        // setup service configuration
        let serviceConfiguration = AWSServiceConfiguration(region: CognitoIdentityUserPoolRegion, credentialsProvider: nil)

        // create pool configuration
        let poolConfiguration = AWSCognitoIdentityUserPoolConfiguration(clientId: CognitoIdentityUserPoolAppClientId,
                                                                        clientSecret: CognitoIdentityUserPoolAppClientSecret,
                                                                        poolId: CognitoIdentityUserPoolId)

        // initialize user pool client
        AWSCognitoIdentityUserPool.register(with: serviceConfiguration, userPoolConfiguration: poolConfiguration, forKey: AWSCognitoUserPoolsSignInProviderKey)

        // fetch the user pool client we initialized in above step
        let pool = AWSCognitoIdentityUserPool(forKey: AWSCognitoUserPoolsSignInProviderKey)
        self.storyboard = UIStoryboard(name: "Main", bundle: nil)
        pool.delegate = self


        return AWSMobileClient.sharedInstance().interceptApplication(
            application, didFinishLaunchingWithOptions:
            launchOptions)
        //return true
    }

    func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
        if let navigationController = self.window?.rootViewController as? UINavigationController {
           if navigationController.visibleViewController is SummaryReportVC ||
              navigationController.visibleViewController is GoalStatusReportVC || navigationController.visibleViewController is YearTotalsReportVC || navigationController.visibleViewController is DailyActivityReportVC {
                return UIInterfaceOrientationMask.all
            } else {
                return UIInterfaceOrientationMask.portrait
            }
        }
        return UIInterfaceOrientationMask.portrait
    }

    func applicationWillResignActive(_ application: UIApplication) {
        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
        // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
    }

    func applicationDidEnterBackground(_ application: UIApplication) {
        // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
        // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
    }

    func applicationWillEnterForeground(_ application: UIApplication) {
        // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
    }

    func applicationDidBecomeActive(_ application: UIApplication) {
        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
    }

    func applicationWillTerminate(_ application: UIApplication) {
        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
    }

}

extension AppDelegate: AWSCognitoIdentityInteractiveAuthenticationDelegate {

    func startPasswordAuthentication() -> AWSCognitoIdentityPasswordAuthentication {
        print("Calling signin VC from app delegate")
        if (self.navigationController == nil) {
            self.navigationController = self.storyboard?.instantiateViewController(withIdentifier: "NCFirst") as? UINavigationController
        }

        if (self.loginViewController == nil) {
            self.loginViewController = self.navigationController?.viewControllers[0] as? LoginVC
        }

        DispatchQueue.main.async {
            self.navigationController!.popToRootViewController(animated: true)
            if (!self.navigationController!.isViewLoaded
                || self.navigationController!.view.window == nil) {
                self.window?.rootViewController?.present(self.navigationController!,
                                                         animated: true,
                                                         completion: nil)
            }

        }
        return self.loginViewController!
    } 
}

这是我的LoginVC代码

class LoginVC: UIViewController {

    @IBOutlet weak var loginButton: UIButton!
    @IBOutlet weak var forgotPasswordLabel: UILabel!
    @IBOutlet weak var signUpLabel: UILabel!
    @IBOutlet weak var emailTF: UITextField!
    @IBOutlet weak var passwordTF: UITextField!
    var passwordAuthenticationCompletion: AWSTaskCompletionSource<AWSCognitoIdentityPasswordAuthenticationDetails>?
    let pool = AWSCognitoIdentityUserPool(forKey: AWSCognitoUserPoolsSignInProviderKey)
    var usernameText: String?

    override func viewDidLoad() {
        super.viewDidLoad()
        self.navigationController?.navigationBar.tintColor = UIColor.white
        self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
        self.navigationController!.navigationBar.setBackgroundImage(UIImage(), for: .default)
        self.navigationController!.navigationBar.shadowImage = UIImage()
        self.navigationController!.navigationBar.isTranslucent = true

        loginButton.addTarget(self, action: #selector(loginUser), for: .touchUpInside)

        loginButton.layer.cornerRadius = 18
        emailTF.addPadding(.left(35))
        passwordTF.addPadding(.left(35))

        let tap = UITapGestureRecognizer(target: self, action: #selector(goToForgotPasswordVC))
        let tap2 = UITapGestureRecognizer(target: self, action: #selector(goToSignUp1VC))
        forgotPasswordLabel.isUserInteractionEnabled = true
        forgotPasswordLabel.addGestureRecognizer(tap)
        signUpLabel.isUserInteractionEnabled = true
        signUpLabel.addGestureRecognizer(tap2)


    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        self.passwordTF.text = nil
        self.emailTF.text = usernameText
    }


    @objc func loginUser() {
        print("Got inside Login func")
        if (self.emailTF.text != nil && self.passwordTF.text != nil) {
            print("Calling login method now")
            let authDetails = AWSCognitoIdentityPasswordAuthenticationDetails(username: self.emailTF.text!, password: self.passwordTF.text! )
            self.passwordAuthenticationCompletion?.set(result: authDetails)

        } else {
            print("Empty fields")
            let alertController = UIAlertController(title: "Missing information",
                                                    message: "Please enter a valid user name and password",
                                                    preferredStyle: .alert)
            let retryAction = UIAlertAction(title: "Retry", style: .default, handler: nil)
            alertController.addAction(retryAction)
        }
    }

    @objc func goToActivitySessionsVC() {
        let storyboard = UIStoryboard(name: "TabBar", bundle: nil)
        let destVC = storyboard.instantiateViewController(withIdentifier: "TabBarVC")
        self.navigationController?.pushViewController(destVC, animated: true)
        self.navigationController?.isNavigationBarHidden = true
    }

    @objc func goToForgotPasswordVC() {
        let storyboard = UIStoryboard(name: "ForgotPassword", bundle: nil)
        let destVC = storyboard.instantiateViewController(withIdentifier: "ForgotPasswordVC")
        self.navigationController?.pushViewController(destVC, animated: true)
    }

    @objc func goToSignUp1VC() {
        let storyboard = UIStoryboard(name: "SignUp", bundle: nil)
        let destVC = storyboard.instantiateViewController(withIdentifier: "SignUp1VC")
        self.navigationController?.pushViewController(destVC, animated: true)
    }

 /*   func checkLoginStatus() {
        if !AWSSignInManager.sharedInstance().isLoggedIn {
            showSignIn()
        }
        else {
            print("Logged In")
            AWSSignInManager.sharedInstance().logout(completionHandler: {(result: Any?, error: Error?) in
                self.showSignIn()
                print("Sign-out Successful");

            })
        }
    }

}
*/
extension LoginVC: AWSCognitoIdentityPasswordAuthentication {

    public func getDetails(_ authenticationInput: AWSCognitoIdentityPasswordAuthenticationInput, passwordAuthenticationCompletionSource: AWSTaskCompletionSource<AWSCognitoIdentityPasswordAuthenticationDetails>) {
        print("Get details called")
        self.passwordAuthenticationCompletion = passwordAuthenticationCompletionSource
        DispatchQueue.main.async {
            if (self.usernameText == nil) {
                self.usernameText = authenticationInput.lastKnownUsername
            }
        }
    }

    public func didCompleteStepWithError(_ error: Error?) {
        print("Did commplete step with error called")
        DispatchQueue.main.async {
            if let error = error as NSError? {
                let alertController = UIAlertController(title: error.userInfo["__type"] as? String,
                                                        message: error.userInfo["message"] as? String,
                                                        preferredStyle: .alert)
                let retryAction = UIAlertAction(title: "Retry", style: .default, handler: nil)
                alertController.addAction(retryAction)

                self.present(alertController, animated: true, completion:  nil)
                 print(error.description)
            } else {
                self.emailTF.text = nil
                self.dismiss(animated: true, completion: nil)
                print("Got in else")
            }
        }
    }
}

要注意的另一件事是,永远不会调用 getDetails ,而 didCompleteStepWithError 方法也不会被调用。当我单击登录按钮时,什么也没有发生。

2 个答案:

答案 0 :(得分:3)

AWS文档非常混乱。经过多次尝试和错误,我能够成功设置Cognito,进行注册,在登录时进行身份验证以及在注销时不进行身份验证。老实说,我不完全理解为什么我称某些事情。我将尽力在这里进行解释。

这是Cognito的工作方式。.首先,它假定用户已经登录并经过身份验证。它检查是否为真。这就是为什么情节提要的入口点是视图控制器,用户在登录后会在之后看到该视图控制器。这全部由启动时在AppDelegate中运行的代码完成。以下是您需要解决的更多信息。

如果用户未登录,则将调用startPasswordAuthentication()。在您的代码中(应该如此),该定义在AppDelegate扩展中为协议AWSCognitoIdentityInteractiveAuthenticationDelegate定义。此外,每次用户需要登录时都会调用startPasswordAuthentication()。如果应用启动后用户已经登录,则不会调用。

关于您的问题的另一条注释-如果未登录用户,则仅在启动时调用getDetails。如果在启动时未登录,则将调用该用户。如果您先登录然后又退出,也会调用它。

因此,请确保故事板的入口点是登录屏幕。

在下面的声明中,我不确定,因此可以随时纠正我:AWS在成功登录后自动访问入口点。 @objc func loginUser()中要执行的所有操作对我来说都是正确的。那就是我所拥有的。但是请确保您的进入点不是登录屏幕,而是成功登录后显示的内容。这是我的故事板的图片:

See the entry point connects to the App View Controller. When the user launches the app but is not signed in, the view pops to the Log In View Controller.

尝试添加以下内容。我不太确定为什么会这样,但是会导致正确的身份验证:

在您的AppDelegate中,在情节提要的变量之后,按如下所示添加一个布尔isInitialized:

     var isInitialized : Bool = false

然后在设置Cognito配置后添加此代码。 (就在didFinishLaunchingWithOptions中的返回语句之前):

    let didFinishLaunching = AWSSignInManager.sharedInstance().interceptApplication(application, didFinishLaunchingWithOptions: launchOptions)

    if (!self.isInitialized) {
        AWSSignInManager.sharedInstance().resumeSession(completionHandler: { (result: Any?, error: Error?) in
            print("Result: \(String(describing: result)) \n Error:\(String(describing: error))")
        })
        self.isInitialized = true
    }

现在用以下内容替换您当前对didFinishLaunching的返回语句:

    return didFinishLaunching

确保在登录屏幕的viewDidLoad()方法中设置了此委托(请注意,您必须导入AWSAuthCore):

   AWSSignInManager.sharedInstance().delegate = self

并按如下方式在您的登录VC中实现该协议:

   extension LoginViewController : AWSSignInDelegate {
       func onLogin(signInProvider: AWSSignInProvider, result: Any?, error: Error?) {
           if error == nil {

           }
       }
   }

将这些变量作为类变量添加到您的视图控制器中,用户可以在登录后看到它们。在下面进行引用。

var user : AWSCognitoIdentityUser?
var userAttributes : [AWSCognitoIdentityProviderAttributeType]?

最后,请确保您正在检查初始屏幕viewWillAppear()中的用户属性。例如,在此方法中调用函数fetchUserAttributes:

func fetchUserAttributes() {
    self.user = AppDelegate.defaultUserPool().currentUser()
    self.user?.getDetails().continueOnSuccessWith(block: { [weak self = self] (task) -> Any? in

        AWSSignInManager.sharedInstance().resumeSession(completionHandler: { (result: Any?, error: Error?) in
            print("Result: \(String(describing: result)) \n Error:\(String(describing: error))")
        })
        guard task.result != nil else {
            // alert error
            return nil
        }
        self?.username = self?.user?.username
        self?.userAttributes = task.result?.userAttributes
        self?.userAttributes?.forEach({ (attribute) in
            print("Name: " + attribute.name!)
        })
        DispatchQueue.main.async {
                self?.setAttributeValues()
            }
        }
        return nil
    })
}

func resetAttributeValues() {
        self.user = nil
        self.userAttributes = nil
}

最后,这是我的退出代码:

    let comp = { [weak self = self] (_ result: Any?, _ error: Error?) -> Void in
        if error == nil {
            self?.user?.signOut()
            self?.resetAttributeValues()
            self?.fetchUserAttributes()
        }
    }
    AWSSignInManager.sharedInstance().logout(completionHandler: comp)

我希望这会有所帮助。我知道这真的很令人困惑,老实说,我只是写这篇文章就感到很困惑。祝您好运,如有任何问题,请随时与我联系。

答案 1 :(得分:0)

您需要调用getDetails。 在示例应用程序中,他们在UserDetailTableViewController中调用getDetails。 在AppDelegate中,在pool.delegate = self下尝试以下代码。

self.pool?.currentUser()?.getDetails()

我推荐了AWS Cognito User Pools in iOS (Swift) app