Closed kipropkorir closed 4 years ago
Did you mean that you want to enable biometrics, but you do not want the passcode fallback, right? If so it's impossible. The passcode fallback option is always provided by the system. Otherwise you can completely disable biometrics and passcode.
I see other apps have done it though, it possible to do it without using this library maybe?
This configuration can also be done via the library. Give it a try.
Please guide me sir, using this library
let keychain = Keychain()
keychain
.accessibility(.whenPasscodeSetThisDeviceOnly, authenticationPolicy: .touchIDAny)
keychain["..."] = ...
I haven't tested but it should work.
It does not work, please add the feature to your library thanks
@kipropkorir
I have confirmed that the above code works. Perhaps you are using the library in the wrong way. I won't change the library because it is working properly. If my perception of your problem is wrong, please provide me more information tells me that.
Hey @kishikawakatsumi , thanks for the update, my case scenario is as below.
keychain .accessibility(.whenPasscodeSetThisDeviceOnly, authenticationPolicy: .userPresence) Using the above code shows both biometric and passocode option.
But when I use:
keychain .accessibility(.whenPasscodeSetThisDeviceOnly, authenticationPolicy: .touchIDAny) or keychain .accessibility(.whenPasscodeSetThisDeviceOnly, authenticationPolicy: .biometricAny)
It doesn't show the passcode option at first but after three failed biometric auth attempts it falls back to "enter passcode", kindly confirm this 👍
Show full code. I believe you do not use return value or running it on the main thread.
Here is the full code:
import Foundation import KeychainAccess import LocalAuthentication
public struct KeyChainKeys { public static let MASTER_MSISDN = "master-msisdn" public static let BIOMETRIC_ID_KEY = "touch-id-store" }
protocol BiometricIDService { var biometricSupported: BiometricType { get }
func checkForBiometricId(completion: @escaping ((_ pincode: String?, _ error: Error?) -> Void))
func storePinForBiometricId(_ pin: String, completion: @escaping ((_ error: Error?) -> Void))
func removeBiometricIdPin(completion: @escaping ((_ error: Error?) -> Void))
}
class BiometricIDServiceDefault: BiometricIDService {
private let bundleId = Bundle.main.bundleIdentifier!
let biometricSupported = LAContext().biometricType
func checkForBiometricId(completion: @escaping ((_ pincode: String?, _ error: Error?) -> Void))
{
let keychain = Keychain(service: bundleId)
DispatchQueue.global().async {
do {
let storedPin = try keychain
.authenticationPrompt(NSLocalizedString("login", comment: "login" ))
.get(KeyChainKeys.BIOMETRIC_ID_KEY)
Logger.debug("pincode: \(String(describing: storedPin))")
DispatchQueue.main.async {
completion(storedPin, nil)
}
} catch let error {
DispatchQueue.main.async {
completion(nil, error)
}
}
}
}
func storePinForBiometricId(_ pin: String, completion: @escaping ((_ error: Error?) -> Void)) {
let keychain = Keychain(service: bundleId)
DispatchQueue.global().async {
do {
try keychain
.accessibility(.whenPasscodeSetThisDeviceOnly, authenticationPolicy: .touchIDAny)
.set(pin, key: KeyChainKeys.BIOMETRIC_ID_KEY)
DispatchQueue.main.async {
completion(nil)
}
} catch let error {
DispatchQueue.main.async {
completion(error)
}
}
}
}
func removeBiometricIdPin(completion: @escaping ((Error?) -> Void)) {
let keychain = Keychain(service: bundleId)
do {
try keychain.remove(KeyChainKeys.BIOMETRIC_ID_KEY)
DispatchQueue.main.async {
completion(nil)
}
} catch let error {
DispatchQueue.main.async {
completion(error)
}
}
}
}
enum BiometricType: String { case none case touchID case faceID }
private extension LAContext { var biometricType: BiometricType { var error: NSError?
guard canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
Logger.debug("Cannot evaluate biometrics policy \(error?.localizedDescription ?? "Error unavailable")")
return .none
}
guard #available(iOS 11.0, *) else {
return .touchID
}
switch biometryType {
case .none:
return .none
case .touchID:
return .touchID
case .faceID:
return .faceID
@unknown default:
assertionFailure("Unknown biometry type detected")
return .none
}
}
}
I use the storePinForBiometricId method to store the pin and use the checkForBiometricId to fetch the key in a different view controller
Thanks. I have run your code. It seems working as expected.
When the first failure, it shows retry and cancel button. Then the second failure, shows only the cancel button. There is no passcode fallback. Isn't this the behavior you want?
The code I tried is here:
BiometricIDServiceDefault().storePinForBiometricId("abcd") { (e) in
print(e) // => nil
DispatchQueue.main.asyncAfter(deadline: .now() + 4) {
BiometricIDServiceDefault().checkForBiometricId { (p, e) in
print(p)
print(e)
}
}
}
First Failure | Second Failure |
---|---|
I have tried disabling by using policy authenticationPolicy: .biometryCurrentSet and authenticationPolicy: .biometryAny but after three failed attempts the app asks for a passcode. How do I remove passcode completely?