One of the most things I was excited about iOS 8 was the ability to use fingerprint sensors on the iPhone 5s and later. Unfortunately I cannot find out what is the required framework for that, nor how I can make authentication. Please help me with:
What framework required for using Touch ID?
How to use its methods and how to authenticate the user?
A code sample would be much appreciated.
More complete snippet, swift style:
func authenticateUser() {
// Get the local authentication context.
let context = LAContext()
// Declare a NSError variable.
var error: NSError?
// Set the reason string that will appear on the authentication alert.
var reasonString = "Authentication is needed to access your notes."
// Check if the device can evaluate the policy.
if context.canEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, error: &error) {
[context .evaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, localizedReason: reasonString, reply: { (success: Bool, evalPolicyError: NSError?) -> Void in
if success {
}
else{
// If authentication failed then show a message to the console with a short description.
// In case that the error is a user fallback, then show the password alert view.
println(evalPolicyError?.localizedDescription)
switch evalPolicyError!.code {
case LAError.SystemCancel.toRaw():
println("Authentication was cancelled by the system")
case LAError.UserCancel.toRaw():
println("Authentication was cancelled by the user")
case LAError.UserFallback.toRaw():
println("User selected to enter custom password")
self.showPasswordAlert()
default:
println("Authentication failed")
self.showPasswordAlert()
}
}
})]
}
else{
// If the security policy cannot be evaluated then show a short message depending on the error.
switch error!.code{
case LAError.TouchIDNotEnrolled.toRaw():
println("TouchID is not enrolled")
case LAError.PasscodeNotSet.toRaw():
println("A passcode has not been set")
default:
// The LAError.TouchIDNotAvailable case.
println("TouchID not available")
}
// Optionally the error description can be displayed on the console.
println(error?.localizedDescription)
// Show the custom alert view to allow users to enter the password.
self.showPasswordAlert()
}
}
Source
The Local Authentication framework provides facilities for requesting authentication from users using Touch ID, following code snipped shows how you should request for authentication.
Objective C
LAContext *myContext = [[LAContext alloc] init];
NSError *authError = nil;
NSString *myReasonString = #"String explaining why app needs authentication";
if ([myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) {
[myContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
localizedReason:myReasonString
reply:^(BOOL succes, NSError *error) {
if (success) {
// User authenticated successfully
} else {
// Authenticate failed
}
}];
} else {
// Could not evaluate policy; check authError
}
Swift
let myContext = LAContext()
var authError: NSError?
// Set the reason string that will appear on the authentication alert.
var myReasonString = "String explaining why app needs authentication"
if myContext.canEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, error: &authError) {
[myContext.evaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, localizedReason: myReasonString, reply: { (success: Bool, evalPolicyError: NSError?) -> Void in
if success {
// User authenticated successfully
} else {
// Authenticate failed
}
})]
} else{
// Could not evaluate policy; check authError
}
You're looking for LocalAuthentication framework (login may be required to see).
Basically you're interested in LAContext class and its
canEvaluatePolicy:error: and
evaluatePolicy:localizedReason:reply: methods.
The canEvaluatePolicy:error: method is used to check if TouchID authentication is available for you to use.
And use evaluatePolicy:localizedReason:reply: to perform actual authentication check
I was looking for an answer in objectiveC with all the possible errors. Didn't find on this post so here it is.
LAContext *myContext = [[LAContext alloc] init];
NSError *authError = nil;
NSString *myLocalizedReasonString = #"Authenticate using your finger";
if ([myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) {
[myContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
localizedReason:myLocalizedReasonString
reply:^(BOOL success, NSError *error) {
if (success) {
NSLog(#"User is authenticated successfully");
} else {
switch (error.code) {
case LAErrorAuthenticationFailed:
NSLog(#"Authentication Failed");
break;
case LAErrorUserCancel:
NSLog(#"User pressed Cancel button");
break;
case LAErrorUserFallback:
NSLog(#"User pressed Enter Password");
break;
case LAErrorPasscodeNotSet:
NSLog(#"Passcode Not Set");
break;
case LAErrorTouchIDNotAvailable:
NSLog(#"Touch ID not available");
break;
case LAErrorTouchIDNotEnrolled:
NSLog(#"Touch ID not Enrolled or configured");
break;
default:
NSLog(#"Touch ID is not configured");
break;
}
NSLog(#"Authentication Fails");
}
}];
} else {
NSLog(#"Can not evaluate Touch ID. This device doesn’t support one");
}
Also make sure to use dispatch_async otherwise your uialert messages will not pop-up on time. See sample code below
-(void)viewWillAppear:(BOOL)animated {
LAContext *myContext = [[LAContext alloc] init];
NSError *authError = nil;
NSString *myLocalizedReasonString = #"Touch ID Test to show Touch ID working in a custom app";
if ([myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) {
[myContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
localizedReason:myLocalizedReasonString
reply:^(BOOL success, NSError *error) {
if (success) {
dispatch_async(dispatch_get_main_queue(), ^{
[self performSegueWithIdentifier:#"Success" sender:nil];
});
} else {
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error"
message:error.description
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil, nil];
[alertView show];
// Rather than show a UIAlert here, use the error to determine if you should push to a keypad for PIN entry.
});
}
}];
} else {
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error"
message:authError.description
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil, nil];
[alertView show];
// Rather than show a UIAlert here, use the error to determine if you should push to a keypad for PIN entry.
});
}
}
Swift 3 version of #txulu response:
public func authenticateUser() {
// Get the local authentication context.
let context = LAContext()
// Declare a NSError variable.
var error: NSError?
// Set the reason string that will appear on the authentication alert.
var reasonString = "Authentication is needed to access your notes."
// Check if the device can evaluate the policy.
if context.canEvaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, error: &error) {
[context .evaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, localizedReason: reasonString, reply: { (success: Bool, evalPolicyError: NSError?) -> Void in
if success {
// TODO - Guardar credencials
}
else{
// If authentication failed then show a message to the console with a short description.
// In case that the error is a user fallback, then show the password alert view.
print(evalPolicyError?.localizedDescription)
switch evalPolicyError!.code {
case LAError.systemCancel.rawValue:
print("Authentication was cancelled by the system")
case LAError.userCancel.rawValue:
print("Authentication was cancelled by the user")
case LAError.userFallback.rawValue:
print("User selected to enter custom password")
//self.showPasswordAlert()
default:
print("Authentication failed")
//self.showPasswordAlert()
}
}
} as! (Bool, Error?) -> Void)]
}
else{
// If the security policy cannot be evaluated then show a short message depending on the error.
switch error!.code{
case LAError.touchIDNotEnrolled.rawValue:
print("TouchID is not enrolled")
case LAError.passcodeNotSet.rawValue:
print("A passcode has not been set")
default:
// The LAError.TouchIDNotAvailable case.
print("TouchID not available")
}
// Optionally the error description can be displayed on the console.
print(error?.localizedDescription)
// Show the custom alert view to allow users to enter the password.
//self.showPasswordAlert()
}
}
Full Swift 5 version example snippet:
import LocalAuthentication
_
func authenticateUser() {
// Get the local authentication context.
let context = LAContext()
// Declare a NSError variable.
var error: NSError?
// Set the reason string that will appear on the authentication alert.
let reasonString = "Authentication is needed to provide access."
// Check if the device can evaluate the policy.
if context.canEvaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, error: &error) {
context.evaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, localizedReason: reasonString, reply: { (success: Bool, evalPolicyError: Error?) -> Void in
if success {
print("Authentication successful! :) ")
} else {
// If authentication failed then show a message to the console with a short description.
// In case that the error is a user fallback, then show the password alert view.
print(evalPolicyError?.localizedDescription)
switch evalPolicyError!._code {
case LAError.authenticationFailed.rawValue:
print("### Authentication was cancelled by the system")
case LAError.userCancel.rawValue:
print("### Authentication was cancelled by the user")
case LAError.userFallback.rawValue:
print("### User selected to enter custom password")
self.showPasswordAlert()
default:
print("### Authentication failed")
self.showPasswordAlert()
}
}
})
} else {
// If the security policy cannot be evaluated then show a short message depending on the error.
switch error!.code{
case LAError.biometryNotEnrolled.rawValue:
print("### TouchID is not enrolled")
case LAError.passcodeNotSet.rawValue:
print("### A passcode has not been set")
default:
// The LAError.TouchIDNotAvailable case.
print("### TouchID not available")
}
// Optionally the error description can be displayed on the console.
print(error?.localizedDescription)
// Show the custom alert view to allow users to enter the password.
self.showPasswordAlert()
}
}
func showPasswordAlert() {
print("### showPasswordAlert")
}
Among these answers I was getting stuck every time I was running the authentication code.
Please make sure you Import LocalAuthentication to use LAContext and privacy check
With Swift 5 this is what worked for me.
import UIKit
import LocalAuthentication
class ViewController: UIViewController {
#IBOutlet weak var lbl: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func checkUserAuthentication(_ sender: Any) {
let context = LAContext()
var error: NSError?
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
let reason = "Identify yourself!"
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) {
[unowned self] success, authenticationError in
DispatchQueue.main.async {
if success {
//Authentication Success
self.lbl.text = "Great!! you are authorised"
} else {
let ac = UIAlertController(title: "Authentication failed", message: "Sorry!", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
self.present(ac, animated: true)
}
}
}
} else {
let ac = UIAlertController(title: "Touch ID not available", message: "Your device is not configured for Touch ID.", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
present(ac, animated: true)
}
}
}
Related
We have a requirement where we have to block the user for 24 hours if Face ID or Touch ID fails for 5 consecutive time.
In Face ID, I am using the fallback to call authentication manager again, and after total of 6 attempts (2 in each call), I block the user in the fallback method itself.
In Touch ID, on third failure, I get a call back to Authentication failed. I call authentication manager again, and on 5th try, I get a call back to Lockout in which I can block the user.
Is there any way common in both Face ID and Touch ID where I can get a callback after every single individual failure so that I can block the user on 5th failure itself?
//MARK:- Check if user has valid biometry, if yes, validate, else, show error
fileprivate func biometricAuthentication(completion: #escaping ((Bool) -> ())){
// addBlurredBackground()
//Check if device have Biometric sensor
if biometryRetryCount == 2 {
authenticationContext.localizedFallbackTitle = "Ok"
} else {
authenticationContext.localizedFallbackTitle = "Retry"
}
let isValidSensor : Bool = authenticationContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error)
if isValidSensor {
//Device have BiometricSensor
authenticationContext.evaluatePolicy(
.deviceOwnerAuthenticationWithBiometrics,
localizedReason: biometryRetryCount == 2 ? "You have been blocked from using the application for next 24 hours. Please come back later" : "Touch / Face ID authentication",
reply: { [unowned self] (success, error) -> Void in
if(success) {
// Touch or Face ID recognized
// self.removeBlurredBackground()
completion(true)
} else {
//If not recognized then
if let error = error {
let msgAndAction = self.errorMessage(errorCode: error._code)
if msgAndAction.0 != "" {
UIApplication.topViewController()?.showAlert(withTitle: "Error", andMessage: msgAndAction.0, andActions: msgAndAction.1)
}
}
completion(false)
}
})
} else {
let msgAndAction = self.errorMessage(errorCode: (error?._code)!)
if msgAndAction.0 != ""{
UIApplication.topViewController()?.showAlert(withTitle: "Error", andMessage: msgAndAction.0, andActions: msgAndAction.1)
}
}
}
The error methods:
//MARK: TouchID error
fileprivate func errorMessage(errorCode:Int) -> (strMessage: String, action: [UIAlertAction]){
var strMessage = ""
let cancelAction = UIAlertAction.init(title: Const.Localize.Common().kCancel, style: .cancel) { (cancelAction) in
}
var actions: [UIAlertAction] = [cancelAction]
switch errorCode {
case LAError.Code.authenticationFailed.rawValue:
biometricAuthentication { (success) in
//
}
case LAError.Code.userCancel.rawValue:
if biometryRetryCount == 2 {
blockUserFor24Hours()
} else {
showAlertOnCancelTapAction()
}
case LAError.Code.passcodeNotSet.rawValue:
strMessage = "Please goto the Settings & Turn On Passcode"
case LAError.Code.userFallback.rawValue:
biometryRetryCount -= 2
if biometryRetryCount == 0 {
blockUserFor24Hours()
} else {
biometricAuthentication { (success) in
//
}
}
default:
strMessage = evaluatePolicyFailErrorMessageForLA(errorCode: errorCode).strMessage
actions = evaluatePolicyFailErrorMessageForLA(errorCode: errorCode).action
}
return (strMessage, actions)
}
func evaluatePolicyFailErrorMessageForLA(errorCode: Int) -> (strMessage: String, action: [UIAlertAction]){
let cancelAction = UIAlertAction.init(title: Const.Localize.Common().kCancel, style: .cancel) { (cancelAction) in
}
var actions: [UIAlertAction] = [cancelAction]
var message = ""
if #available(iOS 11.0, macOS 10.13, *) {
switch errorCode {
case LAError.biometryNotAvailable.rawValue:
message = "Authentication could not start because the device does not support biometric authentication."
case LAError.biometryLockout.rawValue:
showPasscodeScreen()
case LAError.biometryNotEnrolled.rawValue:
message = "You do not have a registered biometric authentication. Kindly go to the settings and setup one"
let settingsAction = UIAlertAction.init(title: Const.Localize.Common().kSetting, style: .default) { (settingsAction) in
UIApplication.shared.openURL(URL(string: UIApplicationOpenSettingsURLString)!)
}
actions.append(settingsAction)
default:
message = "Did not find error code on LAError object"
}
} else {
switch errorCode {
case LAError.touchIDLockout.rawValue:
showPasscodeScreen()
case LAError.touchIDNotAvailable.rawValue:
message = "TouchID is not available on the device"
case LAError.touchIDNotEnrolled.rawValue:
message = "You do not have a registered biometric authentication. Kindly go to the settings and setup one"
let settingsAction = UIAlertAction.init(title: Const.Localize.Common().kSetting, style: .default) { (settingsAction) in
UIApplication.shared.openURL(URL(string: UIApplicationOpenSettingsURLString)!)
}
actions.append(settingsAction)
default:
message = "Did not find error code on LAError object"
}
}
return (message, actions)
}
How to resolve the TouchId error: Domain=com.apple.LocalAuthentication Code=-2 "Canceled by user."
I tried to add local context again:
let myContext = LAContext()
let myLocalizedReasonString = "Please use your last login for Inspyrus Supplier Central."
var authError: NSError?
if #available(iOS 8.0, macOS 10.12.1, *) {
if myContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &authError) {
myContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: myLocalizedReasonString) { success, evaluateError in
DispatchQueue.main.async {
if success {
self.btnLoginClicked(UIButton())
} else {
print(evaluateError?.localizedDescription ?? "Failed to authenticate")
// Fall back to a asking for username and password.
// ...
}
}
}
}
}
You can check if the evaluateError returned from the evaluatePolicy call is a LAError.userCancel.
Something like this:
if success {
//...
}
else if let authError = evaluateError as? LAError {
switch authError.code {
case .userCancel:
// Authentication was canceled by user (e.g. tapped Cancel button).
break
default:
// Other error
break
}
// Or
switch authError {
case LAError.userCancel:
// Authentication was canceled by user (e.g. tapped Cancel button).
break
default:
// Other error
break
}
}
I am using the following code to implement the touchID/passcode authentication in my app
let context : LAContext = LAContext()
var error: NSError?
// Check if the device can evaluate the policy.
if context.canEvaluatePolicy(LAPolicy.deviceOwnerAuthentication, error: &error) {
context.evaluatePolicy(LAPolicy.deviceOwnerAuthentication,
localizedReason: messageString,
reply: { (success: Bool, evalPolicyError: Error?) -> Void in
if success {
// ...
}
else {
switch evalPolicyError!._code {
case LAError.systemCancel.rawValue:
print("Authentication was cancelled by the system")
case LAError.userCancel.rawValue:
print("Authentication was cancelled by the user")
case LAError.userFallback.rawValue:
print("User selected to enter custom password")
OperationQueue.main.addOperation({ () -> Void in
// ...
})
default:
print("Authentication failed")
OperationQueue.main.addOperation({ () -> Void in
// ...
})
}
}
})
}
Is is possible to customize it? For example:
Choose the number of TouchID attempts before switching to passcode?
In the first AlertView let user choose to switch to passcode with a button (in the current implementation you can access the passcode only if you fail the TouchID auth)
When you want to make a change to a PHAsset, you wrap it up in a performChanges block. You get a success Bool and an error NSError in the completion block. Now I would like to show an alert to the user in the case the request failed. This does the trick:
PHPhotoLibrary.sharedPhotoLibrary().performChanges({ () -> Void in
let request = PHAssetChangeRequest(forAsset: asset)
request.creationDate = date
}, completionHandler: { (success: Bool, error: NSError?) -> Void in
dispatch_async(dispatch_get_main_queue()) {
if let error = error {
//present alert
}
}
})
The problem is when the user taps Don't Allow it also presents the alert. I don't want to do that, the user intentionally canceled it so there's no need to inform them it failed. But how can I detect that's what has occurred? The error userInfo is nil, it doesn't seem it provides any useful info to detect that case. Am I missing something?
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
NSLog(#"%ld",(long)status);
switch (status) {
case PHAuthorizationStatusAuthorized:
// code for display photos
NSLog(#"ffefwfwfwef");
case PHAuthorizationStatusRestricted:
break;
case PHAuthorizationStatusDenied:
//code for Dont Allow code
break;
default:
break;
}
}];
This is now possible by checking if the error is a PHPhotosError and if so checking its code to see if it's .userCancelled.
PHPhotoLibrary.shared().performChanges({
let request = PHAssetChangeRequest(forAsset: asset)
request.creationDate = date
}) { success, error in
guard let error = error else { return }
guard (error as? PHPhotosError)?.code != .userCancelled else { return }
DispatchQueue.main.async {
//present alert
}
}
For this i use La Authentication framework but with the help of that i don't got fingerprint data and it just returns me boolean value i.e true and false , so it cant help i want fingerprint data in variable so further i will process it.
Help me.
following is the sample code,
func authenticateUser() {
...
// Check if the device can evaluate the policy.
if context.canEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, error: &error) {
[context .evaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, localizedReason: reasonString, reply: { (success: Bool, evalPolicyError: NSError?) -> Void in
if success {
}
else {
// If authentication failed then show a message to the console with a short description.
// In case that the error is a user fallback, then show the password alert view.
println(evalPolicyError?.localizedDescription)
switch evalPolicyError!.code {
case LAError.SystemCancel.toRaw():
println("Authentication was cancelled by the system")
case LAError.UserCancel.toRaw():
println("Authentication was cancelled by the user")
case LAError.UserFallback.toRaw():
println("User selected to enter custom password")
NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
self.showPasswordAlert()
})
default:
println("Authentication failed")
NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
self.showPasswordAlert()
})
}
}
})]
}
}
You can't, Apple does not allow access to the fingerprint data. This has to do with security.
The iOS SDK only allows you let a user validate them selfs with the Touch ID.