Skip to content

Commit

Permalink
#38 Fixed: touchIDAuthenticationAllowableReuseDuration
Browse files Browse the repository at this point in the history
  • Loading branch information
rushisangani committed Oct 14, 2020
1 parent 0461ce3 commit d9a0c31
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@
D8DB659D1FA46C7E00351903 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1020;
LastUpgradeCheck = 1200;
ORGANIZATIONNAME = "Rushi Sangani";
TargetAttributes = {
D8DB65A51FA46C7E00351903 = {
Expand Down Expand Up @@ -192,6 +192,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
Expand All @@ -218,7 +219,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
Expand Down Expand Up @@ -254,6 +255,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
Expand All @@ -274,7 +276,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
Expand All @@ -287,7 +289,7 @@
D8DB65AF1FA46C7E00351903 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Automatic;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = T5CX5N9HK9;
Expand All @@ -296,7 +298,8 @@
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = BiometricAuthentication/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
"IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 13.1;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MARKETING_VERSION = 3.1.2;
PRODUCT_BUNDLE_IDENTIFIER = com.rushi.BiometricAuthentication;
Expand All @@ -320,7 +323,8 @@
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = BiometricAuthentication/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
"IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 13.1;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MARKETING_VERSION = 3.1.2;
PRODUCT_BUNDLE_IDENTIFIER = com.rushi.BiometricAuthentication;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
LastUpgradeVersion = "1200"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down Expand Up @@ -29,8 +29,6 @@
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
Expand All @@ -51,8 +49,6 @@
ReferencedContainer = "container:BiometricAuthentication.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,15 @@ import LocalAuthentication
/// Authentication Errors
public enum AuthenticationError: Error {

case failed, canceledByUser, fallback, canceledBySystem, passcodeNotSet, biometryNotAvailable, biometryNotEnrolled, biometryLockedout, other
case failed,
canceledByUser,
fallback,
canceledBySystem,
passcodeNotSet,
biometryNotAvailable,
biometryNotEnrolled,
biometryLockedout,
other

public static func initWithError(_ error: LAError) -> AuthenticationError {
switch Int32(error.errorCode) {
Expand Down Expand Up @@ -62,14 +70,19 @@ public enum AuthenticationError: Error {
switch self {
case .canceledByUser, .fallback, .canceledBySystem:
return ""

case .passcodeNotSet:
return isFaceIdDevice ? kSetPasscodeToUseFaceID : kSetPasscodeToUseTouchID

case .biometryNotAvailable:
return kBiometryNotAvailableReason

case .biometryNotEnrolled:
return isFaceIdDevice ? kNoFaceIdentityEnrolled : kNoFingerprintEnrolled

case .biometryLockedout:
return isFaceIdDevice ? kFaceIdPasscodeAuthenticationReason : kTouchIdPasscodeAuthenticationReason

default:
return isFaceIdDevice ? kDefaultFaceIDAuthenticationFailedReason : kDefaultTouchIDAuthenticationFailedReason
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,9 @@ open class BioMetricAuthenticator: NSObject {

// MARK: - Private
private override init() {}
private lazy var context: LAContext? = {
return LAContext()
}()


// MARK: - Public
public var allowableReuseDuration: TimeInterval? = nil {
didSet {
guard let duration = allowableReuseDuration else {
return
}
if #available(iOS 9.0, *) {
self.context?.touchIDAuthenticationAllowableReuseDuration = duration
}
}
}
public var allowableReuseDuration: TimeInterval = 0
}

// MARK:- Public
Expand All @@ -60,80 +48,89 @@ public extension BioMetricAuthenticator {
var isBiometricAuthenticationAvailable = false
var error: NSError? = nil

if LAContext().canEvaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, error: &error) {
if LAContext().canEvaluatePolicy(
LAPolicy.deviceOwnerAuthenticationWithBiometrics,
error: &error
) {
isBiometricAuthenticationAvailable = (error == nil)
}
return isBiometricAuthenticationAvailable
}

/// Check for biometric authentication
class func authenticateWithBioMetrics(reason: String, fallbackTitle: String? = "", cancelTitle: String? = "", completion: @escaping (Result<Bool, AuthenticationError>) -> Void) {
class func authenticateWithBioMetrics(
reason: String,
fallbackTitle: String? = "",
cancelTitle: String? = "",
completion: @escaping (Result<Bool, AuthenticationError>) -> Void
) {

// reason
let reasonString = reason.isEmpty ? BioMetricAuthenticator.shared.defaultBiometricAuthenticationReason() : reason
let reasonString = reason.isEmpty
? BioMetricAuthenticator.shared.defaultBiometricAuthenticationReason()
: reason

// context
var context: LAContext!
if BioMetricAuthenticator.shared.isReuseDurationSet() {
context = BioMetricAuthenticator.shared.context
}else {
context = LAContext()
}
let context = LAContext()
context.touchIDAuthenticationAllowableReuseDuration = BioMetricAuthenticator.shared.allowableReuseDuration
context.localizedFallbackTitle = fallbackTitle

// cancel button title
if #available(iOS 10.0, *) {
context.localizedCancelTitle = cancelTitle
}
context.localizedCancelTitle = cancelTitle

// authenticate
BioMetricAuthenticator.shared.evaluate(policy: .deviceOwnerAuthenticationWithBiometrics, with: context, reason: reasonString, completion: completion)
BioMetricAuthenticator.shared.evaluate(
policy: .deviceOwnerAuthenticationWithBiometrics,
with: context,
reason: reasonString,
completion: completion
)
}

/// Check for device passcode authentication
class func authenticateWithPasscode(reason: String, cancelTitle: String? = "", completion: @escaping (Result<Bool, AuthenticationError>) -> ()) {
class func authenticateWithPasscode(
reason: String,
cancelTitle: String? = "",
completion: @escaping (Result<Bool, AuthenticationError>) -> ()
) {

// reason
let reasonString = reason.isEmpty ? BioMetricAuthenticator.shared.defaultPasscodeAuthenticationReason() : reason
let reasonString = reason.isEmpty
? BioMetricAuthenticator.shared.defaultPasscodeAuthenticationReason()
: reason

let context = LAContext()

// cancel button title
if #available(iOS 10.0, *) {
context.localizedCancelTitle = cancelTitle
}
context.localizedCancelTitle = cancelTitle

// authenticate
if #available(iOS 9.0, *) {
BioMetricAuthenticator.shared.evaluate(policy: .deviceOwnerAuthentication, with: context, reason: reasonString, completion: completion)
} else {
// Fallback on earlier versions
BioMetricAuthenticator.shared.evaluate(policy: .deviceOwnerAuthenticationWithBiometrics, with: context, reason: reasonString, completion: completion)
}
BioMetricAuthenticator.shared.evaluate(
policy: .deviceOwnerAuthentication,
with: context,
reason: reasonString,
completion: completion
)
}

/// checks if device supports face id and authentication can be done
func faceIDAvailable() -> Bool {
let context = LAContext()
var error: NSError?

let canEvaluate = context.canEvaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, error: &error)
if #available(iOS 11.0, *) {
return canEvaluate && context.biometryType == .faceID
}
return canEvaluate
let canEvaluate = context.canEvaluatePolicy(
LAPolicy.deviceOwnerAuthenticationWithBiometrics,
error: &error
)
return canEvaluate && context.biometryType == .faceID
}

/// checks if device supports touch id and authentication can be done
func touchIDAvailable() -> Bool {
let context = LAContext()
var error: NSError?

let canEvaluate = context.canEvaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, error: &error)
if #available(iOS 11.0, *) {
return canEvaluate && context.biometryType == .touchID
}
return canEvaluate
let canEvaluate = context.canEvaluatePolicy(
LAPolicy.deviceOwnerAuthenticationWithBiometrics,
error: &error
)
return canEvaluate && context.biometryType == .touchID
}

/// checks if device has faceId
Expand All @@ -142,10 +139,7 @@ public extension BioMetricAuthenticator {
func isFaceIdDevice() -> Bool {
let context = LAContext()
_ = context.canEvaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, error: nil)
if #available(iOS 11.0, *) {
return context.biometryType == .faceID
}
return false
return context.biometryType == .faceID
}
}

Expand All @@ -162,16 +156,13 @@ extension BioMetricAuthenticator {
return faceIDAvailable() ? kFaceIdPasscodeAuthenticationReason : kTouchIdPasscodeAuthenticationReason
}

/// checks if allowableReuseDuration is set
private func isReuseDurationSet() -> Bool {
guard allowableReuseDuration != nil else {
return false
}
return true
}

/// evaluate policy
private func evaluate(policy: LAPolicy, with context: LAContext, reason: String, completion: @escaping (Result<Bool, AuthenticationError>) -> ()) {
private func evaluate(
policy: LAPolicy,
with context: LAContext,
reason: String,
completion: @escaping (Result<Bool, AuthenticationError>) -> ()
) {

context.evaluatePolicy(policy, localizedReason: reason) { (success, err) in
DispatchQueue.main.async {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0900;
LastUpgradeCheck = 0940;
LastUpgradeCheck = 1200;
ORGANIZATIONNAME = "Rushi Sangani";
TargetAttributes = {
D8DB65861FA46C5C00351903 = {
Expand Down Expand Up @@ -225,6 +225,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
Expand All @@ -250,7 +251,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
Expand Down Expand Up @@ -284,6 +285,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
Expand All @@ -303,7 +305,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
Expand All @@ -318,7 +320,7 @@
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = T5CX5N9HK9;
INFOPLIST_FILE = BiometricAuthenticationExample/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.rosterbuster.BiometricAuthenticationExample;
PRODUCT_NAME = "$(TARGET_NAME)";
Expand All @@ -334,7 +336,7 @@
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = T5CX5N9HK9;
INFOPLIST_FILE = BiometricAuthenticationExample/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.rosterbuster.BiometricAuthenticationExample;
PRODUCT_NAME = "$(TARGET_NAME)";
Expand Down

0 comments on commit d9a0c31

Please sign in to comment.