3
votes

Swift 4 - Ré-authentifier FaceID

J'ai implémenté l'authentification Face ID sur mon application et l'utilisateur est authentifié lorsqu'il appuie sur un bouton. J'ai également implémenté une méthode de déconnexion qui déconnecte l'utilisateur:

@objc func login() {

        //Start Activity Indicator

        self.createIndicator()

        //Define Username and Password Variables

        var user: String!
        var pass: String!


        //Check if User is Authenticating with TouchID, we do this so we know to use credentials from Keychain to make the API call with
        if(loginButton.tag == loginWithTouchID)
        {
            //If Yes, Get the username from Keychain
            if let storedUsername = UserDefaults.standard.value(forKey: "username") as? String {

                //Get Password from Keychain

                do {

                    let passwordItem = KeychainPasswordItem(service: KeychainConfiguration.serviceName,
                                                            account: storedUsername,
                                                            accessGroup: KeychainConfiguration.accessGroup)
                    let keychainPassword = try passwordItem.readPassword()

                    //Store Username and Password from Keychain into Username and Password variables

                    user = storedUsername
                    pass = keychainPassword

                }
                catch {

                    //If something went wrong, stop the Activity Indicator and Alert the user something went wrong.

                    self.stopIndicator()

                    self.customAlert(title: "Error", message: "Error reading password from keychain - \(error)")
                }

            }
        }
        else
        {
            //If we are not using Touch ID, store the username and password text field into the username and password variable to use for the API Call

            user = username.text!
            pass = password.text!
        }

        //Finally call the webservice

        WebService().loginUser(user, password: pass)
        {
            (result: Bool) in
            //If API call is successful
            if(result == true)
            {

                //Stop Activity Indicator

                self.stopIndicator()

                //Check if button tag is create, login or touch ID

                if self.loginButton.tag == self.createLoginButtonTag {

                    //If create, check if a user has login

                    let hasLoginKey = UserDefaults.standard.bool(forKey: "hasLoginKey")
                    if !hasLoginKey {

                        //If not, add username to App Default

                        UserDefaults.standard.setValue(user, forKey: "username")
                    }

                    //Try and save the password to Keychain

                    do {

                        //Create a KeychainPasswordItem

                        let passwordItem = KeychainPasswordItem(service: KeychainConfiguration.serviceName, account: user!, accessGroup: KeychainConfiguration.accessGroup)

                        //Save password to the new KeychainPasswordItem

                        try passwordItem.savePassword(pass!)

                        //Add hasLoginKey bool to App Defaults

                        UserDefaults.standard.set(true, forKey: "hasLoginKey")

                        //Change Login button tag to Login as we do not need to create this user again

                        self.loginButton.tag = self.loginButtonTag

                        //Store Credentials to App Delegate to make API calls down the road.

                        self.appDelegate.username = user
                        self.appDelegate.password = pass

                        self.password.text = ""

                        //Everything has been authenticated, proceed to Dashboad

                        self.performSegue(withIdentifier: "toolbarSegue", sender: nil)


                    } catch {

                        //Something went wrong, alert the user with error.

                        self.customAlert(title: "Error", message: "Error updating keychain - \(error)")

                    }

                }
                    //If Login Button tag with Login
                else if self.loginButton.tag == self.loginButtonTag {

                    //Check if user exists in Keychain

                    if self.checkLogin(username: user, password: pass) {

                        //Store Credentials to App Delegate to make API calls down the road.

                        self.appDelegate.username = user
                        self.appDelegate.password = pass

                        self.password.text = ""

                        //Exisiting user has been authenticated, proceed to Dashboad

                        self.performSegue(withIdentifier: "toolbarSegue", sender: nil)

                    } else {

                        //User does not exist in Keychain, alert user there is an error.

                        self.customAlert(title: "Login Problem", message: "Sorry Login Failed, User and/or Passsword Incorrect")
                    }

                }
                    //If Login Button tag with Touch ID
                else if self.loginButton.tag == self.loginWithTouchID {

                    //Store Credentials to App Delegate to make API calls down the road.

                    self.appDelegate.username = user
                    self.appDelegate.password = pass

                    self.password.text = ""

                    //Touch ID has been authenticated, proceed to Dashboad

                    self.performSegue(withIdentifier: "toolbarSegue", sender: nil)

                }

            }
            else
            {

                //Stop Activity Indicator

                self.stopIndicator()

                //API call was unsuccessful, alert user.

                self.customAlert(title: "Login Problem", message: "Sorry Login Failed, User and/or Passsword Incorrect")

            }

        }


    }

Cependant, lorsque je me déconnecte puis que je tente de me reconnecter, je ne suis pas invité à saisir FaceID, au lieu de cela, il est ignoré et je suis complètement s'identifier. Ma question est de savoir comment éviter cela et inviter l'utilisateur à se connecter chaque fois qu'il appuie sur le bouton?

Voici le code du bouton:

class TouchIDAuth {

    let context = LAContext()

    func canEvaluatePolicy() -> Bool {
        return context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil)
    }

    func authenticateUser(completion: @escaping (NSNumber?) -> Void) {

        guard canEvaluatePolicy() else {
            completion(0)
            return
        }

        context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "Logging in with Touch ID") { (success, evaluateError) in
            if success {
                DispatchQueue.main.async {
                    completion(nil)
                }
            } else {

                let response: NSNumber

                switch evaluateError?._code {
                case Int(kLAErrorAuthenticationFailed):
                    response = 2
                case Int(kLAErrorUserCancel):
                    response = 3
                case Int(kLAErrorUserFallback):
                    response = 4
                default:
                    response = 1
                }

                completion(response)

            }
        }
    }

}

et ma classe TouchIDAuth

@IBAction func loginButtonPressed(_ sender: Any) {

        //Define Button variable from the button that has been tapped.
        let button = sender as! UIButton

        //If the button tag is Touch ID, authenticate the user

        if(button.tag == loginWithTouchID)
        {
            //Check if device is compatible with Touch ID
            if(touchMe.canEvaluatePolicy())
            {
                //Get Response from Touch ID popup
                touchMe.authenticateUser() { responsCode in

                    if let responsCode = responsCode {

                        if(responsCode == 0)
                        {
                            //If Touch ID is not available
                            self.customAlert(title: "Error", message: "Touch ID not available")
                        }
                        else if(responsCode == 1)
                        {
                            //If Touch ID has not been setup
                            self.customAlert(title: "Error", message: "Touch ID may not be configured")
                        }
                        else if(responsCode == 2)
                        {
                            //If Touch ID authentication failed
                            self.customAlert(title: "Error", message: "There was a problem verifying your identity")
                        }

                    } else {

                        //If there is no response code, that means Touch ID was successful in authenticating user and we can now call the login method
                        Timer.scheduledTimer(timeInterval: 0.4, target: self, selector: #selector(Login.login), userInfo: nil, repeats: false)
                    }
                }
            }
        }
        else
        {
            Timer.scheduledTimer(timeInterval: 0.4, target: self, selector: #selector(Login.login), userInfo: nil, repeats: false)
        }


    }

Voici la méthode de connexion qui est appelée dans la méthode bouton pressé

dismiss(animated: true, completion: {

UserDefaults.standard.set(false, forKey: "hasLoginKey")

})


3 commentaires

Utilisez-vous la fonction loginButtonPressed pour un autre bouton?


pour une autre méthode oui, je poste cette méthode maintenant.


les évaluations de politique de contexte sont réutilisées car vous utilisez le même LAContext . Réinitialisez le contexte pour résoudre votre problème


3 Réponses :


2
votes

Je pense que vous modifiez la balise du bouton de connexion dans login , dans cette ligne:

 if(button.tag == loginWithTouchID) {
      // login with touchID
else {
      // authenticate the user
}

C'est pourquoi lorsque l'utilisateur clique sur le bouton de connexion le suivant temps, il authentifiera directement l'utilisateur (car la condition est false):

//Change Login button tag to Login as we do not need to create this user again, as you have specified

self.loginButton.tag = self.loginButtonTag

Donc je pense que vous ne devriez pas changer le self .loginButton.tag


0 commentaires

0
votes

Remarque: à la lumière de votre problème

Cependant, lorsque je me déconnecte puis que je tente de me reconnecter, je ne suis pas invité à saisir FaceID, mais il est ignoré et je suis complètement login.

Normalement, lorsque vous mettez à jour UserDefaults (c'est-à-dire en définissant de nouvelles valeurs / objets par rapport aux clés), vous ajoutez le morceau de code suivant pour que le changement prenne effet.

XXX

Alors, ajoutez-le après

UserDefaults.standard.set(false, forKey: "hasLoginKey") // 1


UserDefaults.standard.setValue(user, forKey: "username") // 2


UserDefaults.standard.set(true, forKey: "hasLoginKey") // 3

J'espère que cela vous aidera.


1 commentaires

La documentation Apple indique que "cette méthode est inutile et ne doit pas être utilisée ".



0
votes

J'ai également implémenté une méthode de déconnexion qui déconnecte l'utilisateur:

ignorer (animé: vrai, achèvement: {

UserDefaults.standard.set (false, forKey: "hasLoginKey")

})

Mon application avait le même comportement. Demander une fois l'authentification FaceID et non après la déconnexion.

Ajoutez TouchIDAuth.context = LAContext () (change let en var) dans votre méthode de déconnexion, comme mentionné par @Pranav Kasetti, le contexte d'authentification sera réinitialisé et votre utilisateur sera invité à nouveau pour utiliser FaceID (ou TouchID).


0 commentaires