AWS Cognito登录不起作用(Swift-iOS) [英] AWS Cognito sign in not working (Swift-iOS)

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

问题描述

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

I've integrated cognito into my xcode project. The sign up/password update features are working correctly. However I can't seem to get the sign in process to work. I turned on the logs and I get the following error

{"__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 委托。

I have also implemented the AWSCognitoIdentityInteractiveAuthenticationDelegate delegate in the AppDelegate script.

这是AppDelegate代码

Here's the AppDelegate code

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代码

Here's my LoginVC code

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 也不会调用方法。当我单击登录按钮时,什么也没有发生。

One other thing to note is that getDetails never gets called and so does the didCompleteStepWithError method. When I click the sign in button, nothing happens.

推荐答案

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

The AWS documentation is quite confusing. After much trial and error, I was able to successfully set up Cognito, sign up, authenticate on log in, and un-authenticate on sign out. To be quite honest, I don't fully understand why I call certain things. To the best of my ability, I will explain here.

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

Here's how Cognito works.. First it assumes that the user is already logged in and authenticated. It checks to see if this is true. This is the reason why the entry point for your storyboard is the view controller that users see after they are logged in. This is all done with the code that runs in your AppDelegate on launch. More on what you need to fix in that below.

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

If the user is not logged in, startPasswordAuthentication() will be called. In your code, (as it should be) this defined in the extension of AppDelegate for the protocol AWSCognitoIdentityInteractiveAuthenticationDelegate. Furthermore, startPasswordAuthentication() is called every time the user needs to log in. If the user is already logged in once the app starts, this is not called.

另一个说明关于您的问题-如果未登录用户,则仅在启动时调用getDetails。如果在启动时未登录用户,则调用此方法。

Another note on your question - getDetails is only called on launch if the user is not signed in. If on launch the user is not signed in, then this is called. It is also called if you are signed in and then you sign out.

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

So make sure the entry point for your storyboard is the logged-in screen.

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

On the statement that follows I am not entirely sure, so feel free to correct me if so: AWS automatically accesses the entry point upon successful log-in. Everything you are going in your @objc func loginUser() looks correct to me. That's what I have. But make sure your entry point is not the log-in screen but rather what shows after successful log in. Here is a picture of my storyboard:

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

Try adding the following. I am not quite sure why exactly this works, but it results in proper authentication:

在您的AppDelegate中,在情节提要的变量之后,立即添加一个布尔值isInitialized这样:

In your AppDelegate, right after your variable for the storyboard, add a boolean isInitialized as such:

     var isInitialized : Bool = false

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

Then add this code after you set up your Cognito configuration. (right before your return statement in 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的返回语句:

Now replace the return statement you currently have for didFinishLaunching with the following:

    return didFinishLaunching

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

Make sure you have this delegate set in your viewDidLoad() method for your login screen (Note you have to import AWSAuthCore):

   AWSSignInManager.sharedInstance().delegate = self

并在登录VC中实现该协议,如下所示:

and implement the protocol in your log-in VC as such:

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

           }
       }
   }

添加这些变量作为用户登录到您的视图控制器后的类变量。在下面引用。

Add these variables as class variables to your view controller that users see after they are logged in. They are referenced below.

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

/*
     * :name: defaultUserPool
     * :description: Returns the cognito identity pool for the global pool ID.
     * :return: A Cognito Identity pool instantiation
     */
    class func defaultUserPool() -> AWSCognitoIdentityUserPool {
        return AWSCognitoIdentityUserPool(forKey: userPoolID)
    }

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

Finally, make sure that you are checking the user attributes in the initial screen in viewWillAppear(). For example call the function fetchUserAttributes in this method:

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
}

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

Finally, here is my code for signing out:

    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)

我希望这有帮助。我知道这确实令人困惑,老实说,我只是写这篇文章就感到很困惑。祝您好运,如有任何疑问,请随时给我发消息。

I hope this helps. I understand this is really confusing, and to be honest, I was quite confused just writing this.. Good luck and feel free to message me with any questions.

这篇关于AWS Cognito登录不起作用(Swift-iOS)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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