Add new LocalAuthenticationService type

This commit is contained in:
Daniel Saidi 2022-04-29 10:57:09 +02:00
parent 8de53563f7
commit df8fbaba07
4 changed files with 102 additions and 47 deletions

View File

@ -6,6 +6,11 @@
### ✨ New features
* `LAContext+Async` adds an async policy evaluation function.
* `LocalAuthenticationService` is a new service that lets you use any local authentication policy.
### 💡 Behavior changes
* `BiometricAuthenticationService` now inherits and specializes `LocalAuthenticationService`.

View File

@ -19,14 +19,24 @@ public protocol AuthenticationService: AnyObject {
typealias AuthResult = Result<Void, Error>
/**
Authenticate the user for a certain authentication type.
Authenticate the user for a certain ``Authentication``.
`reason` can be used to display information to the user.
- Parameters:
- auth: The authentication type to evaluate.
- reason: The localized reason to show to the user.
- completion: The completion block to call once authentication is done.
*/
func authenticateUser(for auth: Authentication, reason: String, completion: @escaping AuthCompletion)
func authenticateUser(
for auth: Authentication,
reason: String,
completion: @escaping AuthCompletion)
/**
Check if the service instance can authenticate the user.
Whether or not the service can authenticate the current
user for a certain ``Authentication`` type.
- Parameters:
- auth: The authentication type to evaluate.
*/
func canAuthenticateUser(for auth: Authentication) -> Bool
}

View File

@ -13,52 +13,13 @@ import LocalAuthentication
This authentication service uses `LocalAuthentication` such
as `FaceID` or `TouchID` to authenticate the user.
*/
public class BiometricAuthenticationService: AuthenticationService {
public init() {}
private let policy = LAPolicy.deviceOwnerAuthenticationWithBiometrics
public class BiometricAuthenticationService: LocalAuthenticationService {
/**
Authenticate the user for a certain authentication type.
`reason` can be used to display information to the user.
Create a service instance.
*/
public func authenticateUser(for auth: Authentication, reason: String, completion: @escaping AuthCompletion) {
guard canAuthenticateUser(for: auth) else { return completion(.failure(AuthError.unsupportedAuthentication)) }
performAuthentication(for: auth, reason: reason) { result in
DispatchQueue.main.async { completion(result) }
}
}
/**
Check if the service instance can authenticate the user.
For instance, a user can disable authentication for the
app, which means that the service can no longer fulfill
it's intended use.
*/
public func canAuthenticateUser(for auth: Authentication) -> Bool {
var error: NSError?
return LAContext().canEvaluatePolicy(policy, error: &error)
}
/**
Authenticate the user for a certain authentication type,
regardless of if this service can authenticate the user
for the provided authentication type or not.
This is a way to bypass any particular rules and states
of the service and is not part of the service protocol.
*/
public func performAuthentication(for auth: Authentication, reason: String, completion: @escaping AuthCompletion) {
LAContext().evaluatePolicy(policy, localizedReason: reason) { result, error in
if let error = error { return completion(.failure(error)) }
if result == false { return completion(.failure(AuthError.authenticationFailed)) }
completion(.success(()))
}
public init() {
super.init(policy: .deviceOwnerAuthenticationWithBiometrics)
}
}
#endif

View File

@ -0,0 +1,79 @@
//
// LocalAuthenticationService.swift
// SwiftKit
//
// Created by Daniel Saidi on 2022-04-29.
// Copyright © 2022 Daniel Saidi. All rights reserved.
//
#if os(iOS) || os(macOS)
import LocalAuthentication
/**
This service uses `LocalAuthentication` to authenticate the
current user.
*/
open class LocalAuthenticationService: AuthenticationService {
/**
Create a service instance.
- Parameters:
- policy: The authentication policy to use.
*/
public init(policy: LAPolicy) {
self.policy = policy
}
private let policy: LAPolicy
/**
Authenticate the user for a certain ``Authentication``.
- Parameters:
- auth: The authentication type to evaluate.
- reason: The localized reason to show to the user.
*/
open func authenticateUser(for auth: Authentication, reason: String, completion: @escaping AuthCompletion) {
guard canAuthenticateUser(for: auth) else { return completion(.failure(AuthError.unsupportedAuthentication)) }
performAuthentication(for: auth, reason: reason) { result in
DispatchQueue.main.async { completion(result) }
}
}
/**
Check if the service instance can authenticate the user
for a certain ``Authentication``.
For biometric authentication, a user can disable system
authentication for an app, which means that the service
can no longer fulfill it's intended use.
- Parameters:
- auth: The authentication type to evaluate.
*/
open func canAuthenticateUser(for auth: Authentication) -> Bool {
var error: NSError?
return LAContext().canEvaluatePolicy(policy, error: &error)
}
/**
Authenticate the user for a certain `` authentication type,
regardless of if this service can authenticate the user
for the provided authentication type or not.
This is a way to bypass any particular rules and states
of the service and can be used to e.g. mock .
*/
open func performAuthentication(for auth: Authentication, reason: String, completion: @escaping AuthCompletion) {
LAContext().evaluatePolicy(policy, localizedReason: reason) { result, error in
if let error = error { return completion(.failure(error)) }
if result == false { return completion(.failure(AuthError.authenticationFailed)) }
completion(.success(()))
}
}
}
#endif