Simply in code I use it like this:
let context = LAContext()
if context.canEvaluatePolicy(.deviceOwnerAuthentication, error: nil)
context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: "jjj") { success, error in
print(error)
print(success)
}
}
Then user can see:
Everything is fine until user tap Cancel. Then I display label:
"Please use biometrics to authenticate". NOW I need to get a callback after user was authenticated at any time after first try was cancelled. How can I detect this?
You don’t need a “callback” for this. If the user refuses authentication in response to the dialog, the only way authentication can happen is in Settings, i.e. outside your app. So just check for authentication every time your app comes to the foreground.
Try with code Obj-C, I think that Swift is the same logic
self.context = [[LAContext alloc] init];
[self.context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
localizedReason:strMessage
reply:^(BOOL success, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
if (error.code == LAErrorUserFallback) {
//Do some thing
}else if (error.code == LAErrorAuthenticationFailed) {
//User authen failed
}else if (error.code == LAErrorUserCancel) {
//User cancel
}else{
//Something wrong...
}
return;
}
if (success) {
//Success
} else {
//Failed
return;
}
});
}];
Related
I want to take different actions to users if device support Face ID or Touch ID.
When using the Face ID, iOS asking permission to use. (unlike Touch ID).
And if the user denies permission, context.biometryType return LABiometryTypeNone.
Is there anyway to check Touch ID or Face ID supported by device.
LAContext *context = [[LAContext alloc] init];
NSError *error;
if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]) {
}
if (#available(iOS 11.0, *)) {
if (context.biometryType == LABiometryTypeFaceID) {
// support FaceID
}
}
// support TouchID
Console output
(lldb) po error
Error Domain=com.apple.LocalAuthentication Code=-6 "User has denied the use of biometry for this app." UserInfo={NSLocalizedDescription=User has denied the use of biometry for this app.}
(lldb) po context.biometryType
LABiometryTypeNone
NOTE: I Don't want to use passcode authentication. I just need to know
device is support Touch ID or Face ID
Use property biometryType of LAContext to check and evaluate available biometric policy. (For a passcode authentication , when biometric fails, use: LAPolicyDeviceOwnerAuthentication)
Try this and see:
LAContext *laContext = [[LAContext alloc] init];
NSError *error;
// For a passcode authentication , when biometric fails, use: LAPolicyDeviceOwnerAuthentication
//if ([laContext canEvaluatePolicy: LAPolicyDeviceOwnerAuthentication error:&error]) {
if ([laContext canEvaluatePolicy: LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]) {
if (error != NULL) {
// handle error
} else {
if (#available(iOS 11, *)) {
if (laContext.biometryType == LABiometryTypeFaceID) {
//localizedReason = "Unlock using Face ID"
NSLog(#"FaceId support");
} else if (laContext.biometryType == LABiometryTypeTouchID) {
//localizedReason = "Unlock using Touch ID"
NSLog(#"TouchId support");
} else {
//localizedReason = "Unlock using Application Passcode"
NSLog(#"No biometric support or Denied biometric support");
}
} else {
// Fallback on earlier versions
}
[laContext evaluatePolicy: LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:#"Test Reason" reply:^(BOOL success, NSError * _Nullable error) {
if (error != NULL) {
// handle error
} else if (success) {
// handle success response
} else {
// handle false response
}
}];
}
}
No. If the user denies the Privacy Permissions prompt with the NSFaceIDUsageDescription, all future instances of LAContext will have a biometryType property of .none once canEvalutePolicy:error: is called on them.
It's too late but I hope this can help whoever have the same issue.
LAContext().canEvaluatePolicy(.deviceOwnerAuthentication, error: nil)
LAContext().canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil)
the difference between deviceOwnerAuthentication & deviceOwnerAuthenticationWithBiometrics that the first one will tell you the device have authentication method or not .. second one have same behavior but only works is user accepted the permission.
enum BiometryResult: Int {
case faceID
case touchID
case notExist
}
class func biometryType() -> BiometryResult {
let context = LAContext()
if (context.canEvaluatePolicy(.deviceOwnerAuthentication, error: nil)) {
if (context.biometryType == LABiometryType.faceID) {
return .faceID
} else if (context.biometryType == LABiometryType.touchID) {
return .touchID
} else {
return .notExist
}
}
return .notExist
}
I am using facebook sdk 4.7 and I need to check if accesstoken is expired.
FBSDKAccessToken *access_token = [FBSDKAccessToken currentAccessToken];
if (access_token != nil) {
//user is not logged in
//How to Check if access token is expired?
if ([access_token isExpired]) {
//access token is expired ......
//
}
}
And if I success with that I have to log the user again.
The SDK gives an expiration_date.how can that help? The device may have wrong date.
Assuming user has been logged in with Facebook before and has [FBSDKAccessToken currentAccessToken] != nil(I am not going into details here, because login via FB is another story).
In my app, I do the following to make sure the FB access token is always valid and synced with my app server.
To keep it simple, all the code below is in AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// ...
/**
Add observer BEFORE FBSDKApplicationDelegate's
application:didFinishLaunchingWithOptions: returns
FB SDK sends the notification at the time it
reads token from internal cache, so our app has a chance
to be notified about this.
*/
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(fbAccessTokenDidChange:)
name:FBSDKAccessTokenDidChangeNotification
object:nil];
return [[FBSDKApplicationDelegate sharedInstance] application: application didFinishLaunchingWithOptions: launchOptions];
}
- (void)fbAccessTokenDidChange:(NSNotification*)notification
{
if ([notification.name isEqualToString:FBSDKAccessTokenDidChangeNotification]) {
FBSDKAccessToken* oldToken = [notification.userInfo valueForKey: FBSDKAccessTokenChangeOldKey];
FBSDKAccessToken* newToken = [notification.userInfo valueForKey: FBSDKAccessTokenChangeNewKey];
NSLog(#"FB access token did change notification\nOLD token:\t%#\nNEW token:\t%#", oldToken.tokenString, newToken.tokenString);
// initial token setup when user is logged in
if (newToken != nil && oldToken == nil) {
// check the expiration data
// IF token is not expired
// THEN log user out
// ELSE sync token with the server
NSDate *nowDate = [NSDate date];
NSDate *fbExpirationDate = [FBSDKAccessToken currentAccessToken].expirationDate;
if ([fbExpirationDate compare:nowDate] != NSOrderedDescending) {
NSLog(#"FB token: expired");
// this means user launched the app after 60+ days of inactivity,
// in this case FB SDK cannot refresh token automatically, so
// you have to walk user thought the initial log in with FB flow
// for the sake of simplicity, just logging user out from Facebook here
[self logoutFacebook];
}
else {
[self syncFacebookAccessTokenWithServer];
}
}
// change in token string
else if (newToken != nil && oldToken != nil
&& ![oldToken.tokenString isEqualToString:newToken.tokenString]) {
NSLog(#"FB access token string did change");
[self syncFacebookAccessTokenWithServer];
}
// moving from "logged in" state to "logged out" state
// e.g. user canceled FB re-login flow
else if (newToken == nil && oldToken != nil) {
NSLog(#"FB access token string did become nil");
}
// upon token did change event we attempting to get FB profile info via current token (if exists)
// this gives us an ability to check via OG API that the current token is valid
[self requestFacebookUserInfo];
}
}
- (void)logoutFacebook
{
if ([FBSDKAccessToken currentAccessToken]) {
[[FBSDKLoginManager new] logOut];
}
}
- (void)syncFacebookAccessTokenWithServer
{
if (![FBSDKAccessToken currentAccessToken]) {
// returns if empty token
return;
}
// BOOL isAlreadySynced = ...
// if (!isAlreadySynced) {
// call an API to sync FB access token with the server
// }
}
- (void)requestFacebookUserInfo
{
if (![FBSDKAccessToken currentAccessToken]) {
// returns if empty token
return;
}
NSDictionary* parameters = #{#"fields": #"id, name"};
FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:#"me"
parameters:parameters];
[request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {
NSDictionary* user = (NSDictionary *)result;
if (!error) {
// process profile info if needed
}
else {
// First time an error occurs, FB SDK will attemt to recover from it automatically
// via FBSDKGraphErrorRecoveryProcessor (see documentation)
// you can process an error manually, if you wish, by setting
// -setGraphErrorRecoveryDisabled to YES
NSInteger statusCode = [(NSString *)error.userInfo[FBSDKGraphRequestErrorHTTPStatusCodeKey] integerValue];
if (statusCode == 400) {
// access denied
}
}
}];
}
Each time you think it is good time to check FB token (e.g. an app was in background for a while), call -requestFacebookUserInfo. This will submit Open Graph request and returns an error if token is invalid/expired.
for checking facebook permission..& give a permission...if permission exist then automatically get accesstoken other wise ask for login...
For Swift
var login: FBSDKLoginManager = FBSDKLoginManager()
login.logInWithReadPermissions(["public_profile", "email"], handler: { (result:FBSDKLoginManagerLoginResult!, error:NSError!) -> Void in
if (error != nil)
{
//Process error
}
else if result.isCancelled
{
//Handle cancellations
}
else
{
// If you ask for multiple permissions at once, you
// should check if specific permissions missing
if result.grantedPermissions.contains("email"){
//Do work
}
}
})
For Objective c:
check Permission like this. following code use ..
if ([[FBSDKAccessToken currentAccessToken]hasGranted:#"email"])
{
// add your coding here after login call this block automatically.
}
else
{
//login code **//if accesstoken expired...then call this block**
FBSDKLoginManager *loginManager = [[FBSDKLoginManager alloc] init];
[loginManager logInWithReadPermissions:#[#"public_profile", #"email"] handler:^(FBSDKLoginManagerLoginResult *result, NSError *error)
}];
}
I am trying to bring obj-c code to swift (facebook ios sdk), but autocomplete(intellisense) does not work in handler and I get an error (marked in code) : Set NSObject does not have a member named 'containsObject'
#IBAction func loginWithFacebook(sender: AnyObject) {
/*
FBSDKLoginManager *login = [[FBSDKLoginManager alloc] init];
[login logInWithReadPermissions:#[#"email"] handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) {
if (error) {
// Process error
} else if (result.isCancelled) {
// Handle cancellations
} else {
// If you ask for multiple permissions at once, you
// should check if specific permissions missing
if ([result.grantedPermissions containsObject:#"email"]) {
// Do work
}
}
}];
*/
let fbLoginManager = FBSDKLoginManager()
fbLoginManager.logInWithReadPermissions(["email"], handler: {
result, error in
if ((error) != nil){
}
else if (result.isCancelled){
} else {
if(result.grantedPermissions.containsObject("email")){ //<-- error here
}
}
})
}
Because Swift 1.2 automatically casts all NSSet objects (the ones that coming from external libs/sdks/frameworks etc...) to Set structure you need to call contains instead of containsObject for such things (doc).
I know this may be a simple question but I can't find what I'm looking for on the internet. I'm using the LocalAuthentication framework from iOS 8 in my project and my code is here:
if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]) {
[context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
localizedReason:#"Let's just quickly check that you are the device owner."
reply:^(BOOL success, NSError *error) {
dispatch_async (dispatch_get_main_queue(), ^{
if (error) {
// Error occurred
} else if (success) {
// Device owner, success!
} else {
// Not device owner
}
});
}];
}
But I want to know when the user tapped 'Enter password' which is LAErrorUserFallback. However I just want to know how to compare the error variable I have there with the LAErrorUserFallback to see the outcome error.
I have tried this:
if (error) {
if (error == LAErrorUserFallback) {
// User tapped 'Enter password'
}
}
but obviously these are not the same type.
Any help?
According to the docs, that's the error code.
So try something like error.code == LAErrorUserFallback.
I am working on an app which would require Touch ID Authentication, so is there any way i can use Touch ID (fingerprint scanner) in the simulator ?
Also, please do share some kind of example code for using LocalAuthentication framework.
XCODE 7 beta supports testing the Touch ID Authentication in iPhone Simulator.You can try this for your testing.
[Screenshot 1]
[Screenshot 2]
As of Xcode 7 the Simulator supports 'touchID'. Answer below contains further info.
As of the latest beta (6) there is no way to simulate a fingerprint scan on the simulator. To be honest I doubt this will be included even in later betas.
You will need to test on device.
To use the Authentication framework right now you need:
* XCode 6
* iPhone 5s with iOS 8
The steps you need to perform are:
Find out whether the device supports fingerprint validation and whether a fingerprint is enrolled:
#import LocalAuthentication;
// Get the local authentication context:
LAContext *context = [[LAContext alloc] init];
// Test if fingerprint authentication is available on the device and a fingerprint has been enrolled.
if ([context canEvaluatePolicy: LAPolicyDeviceOwnerAuthenticationWithBiometrics error:nil])
{
NSLog(#"Fingerprint authentication available.");
}
Validate a fingerprint only:
[context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:#"Authenticate for server login" reply:^(BOOL success, NSError *authenticationError){
if (success) {
NSLog(#"Fingerprint validated.");
}
else {
NSLog(#"Fingerprint validation failed: %#.", authenticationError.localizedDescription);
}
}];
Validate a fingerprint or the device’s passcode depending on the user’s choice:
This is a little beyond the scope of a question here, please find more information at: https://www.secsign.com/fingerprint-validation-as-an-alternative-to-passcodes/
For xCode 12
Features -> Touch ID
In Objective c
#import LocalAuthentication;
#interface EnterPasscodeVC ()
-(void)viewWillAppear:(BOOL)animated {
LAContext *myContext = [[LAContext alloc] init];
NSError *authError = nil;
NSString *myLocalizedReasonString = #"Authentication is required to access your QPay Apps.";
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];
switch (error.code) {
case LAErrorAuthenticationFailed:
NSLog(#"Authentication Failed");
// Rather than show a UIAlert here, use the error to determine if you should push to a keypad for PIN entry.
break;
case LAErrorUserCancel:
NSLog(#"User pressed Cancel button");
break;
case LAErrorUserFallback:
NSLog(#"User pressed \"Enter Password\"");
break;
default:
NSLog(#"Touch ID is not configured");
break;
}
NSLog(#"Authentication Fails");
});
}
}];
} 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.
});
}
}
In Swift
import LocalAuthentication
override func viewDidLoad() {
super.viewDidLoad()
authenticateUser()
}
// MARK: Method implementation
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 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 {
// If authentication was successful then load the data.
NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
self.loadData()
})
}
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")
NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
self.showPasswordAlert()
})
default:
print("Authentication failed")
NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
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.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.
showPasswordAlert()
}
}
func showPasswordAlert() {
let passwordAlert : UIAlertView = UIAlertView(title: "TouchIDDemo", message: "Please type your password", delegate: self, cancelButtonTitle: "Cancel", otherButtonTitles: "Okay")
passwordAlert.alertViewStyle = UIAlertViewStyle.SecureTextInput
passwordAlert.show()
}
func loadData(){
if appDelegate.checkIfDataFileExists() {
self.dataArray = NSMutableArray(contentsOfFile: appDelegate.getPathOfDataFile())
self.tblNotes.reloadData()
}
else{
print("File does not exist")
}
}
// MARK: UIAlertViewDelegate method implementation
func alertView(alertView: UIAlertView!, clickedButtonAtIndex buttonIndex: Int) {
if buttonIndex == 1 {
if !alertView.textFieldAtIndex(0)!.text!.isEmpty {
if alertView.textFieldAtIndex(0)!.text == "appcoda" {
loadData()
}
else{
showPasswordAlert()
}
}
else{
showPasswordAlert()
}
}
}