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 Réponses :
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
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.
La documentation Apple indique que "cette méthode est inutile et ne doit pas être utilisée ".
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).
Utilisez-vous la fonction
loginButtonPressedpour 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