Add new LocalAuthenticationService type
This commit is contained in:
parent
8de53563f7
commit
df8fbaba07
|
@ -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`.
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
Loading…
Reference in New Issue