Update auth documentation

This commit is contained in:
Daniel Saidi 2021-09-08 11:06:16 +02:00
parent 9df955bebc
commit 50e30153bc
6 changed files with 62 additions and 23 deletions

View File

@ -11,9 +11,9 @@ import Foundation
/** /**
This struct represents a unique authentication. This struct represents a unique authentication.
This struct currently only has an `id` but it is still used This struct currently only has an `id` but is still used to
to be able to extend the authentication information without be able to extend the authentication info without having to
having to change any authentication protocols. change any authentication protocols.
*/ */
public struct Authentication { public struct Authentication {

View File

@ -20,7 +20,8 @@ public protocol AuthenticationService: AnyObject {
/** /**
Authenticate the user for a certain authentication type. Authenticate the user for a certain authentication type.
`reason` can be used as display information to the user.
`reason` can be used to display information to the user.
*/ */
func authenticateUser(for auth: Authentication, reason: String, completion: @escaping AuthCompletion) func authenticateUser(for auth: Authentication, reason: String, completion: @escaping AuthCompletion)

View File

@ -29,6 +29,13 @@ public class BiometricAuthenticationService: AuthenticationService {
// MARK: - Public functions // MARK: - Public functions
/**
Authenticate the user for a certain authentication type,
provided that the service can authenticate the user for
the provided authentication type.
`reason` can be used to display information to the user.
*/
public func authenticateUser(for auth: Authentication, reason: String, completion: @escaping AuthCompletion) { public func authenticateUser(for auth: Authentication, reason: String, completion: @escaping AuthCompletion) {
guard canAuthenticateUser(for: auth) else { return completion(.failure(AuthError.unsupportedAuthentication)) } guard canAuthenticateUser(for: auth) else { return completion(.failure(AuthError.unsupportedAuthentication)) }
performAuthentication(for: auth, reason: reason) { result in performAuthentication(for: auth, reason: reason) { result in
@ -36,11 +43,23 @@ public class BiometricAuthenticationService: AuthenticationService {
} }
} }
/**
Check if the service instance can authenticate the user
for a certain authentication type.
*/
public func canAuthenticateUser(for auth: Authentication) -> Bool { public func canAuthenticateUser(for auth: Authentication) -> Bool {
var error: NSError? var error: NSError?
return LAContext().canEvaluatePolicy(policy, error: &error) 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) { public func performAuthentication(for auth: Authentication, reason: String, completion: @escaping AuthCompletion) {
LAContext().evaluatePolicy(policy, localizedReason: reason) { result, error in LAContext().evaluatePolicy(policy, localizedReason: reason) { result, error in
if let error = error { return completion(.failure(error)) } if let error = error { return completion(.failure(error)) }

View File

@ -18,7 +18,7 @@ import Foundation
to perform biometric authentication to access critical data. to perform biometric authentication to access critical data.
Note that you can't rely on a cached authentication service Note that you can't rely on a cached authentication service
to clear its cached state. Call `resetUserAuthentication()` to clear its cached state. Call `resetUserAuthentications()`
or `resetUserAuthentication(for:)` as soon as this state is or `resetUserAuthentication(for:)` as soon as this state is
considered to be invalid, e.g. when your app is send to the considered to be invalid, e.g. when your app is send to the
background and a new user can open the app at a later time. background and a new user can open the app at a later time.
@ -26,19 +26,20 @@ import Foundation
public protocol CachedAuthenticationService: AuthenticationService { public protocol CachedAuthenticationService: AuthenticationService {
/** /**
Check if the user has already been authenticated for an Check if the service has already authenticated the user
authentication type. for a certain authentication type.
*/ */
func isUserAuthenticated(for auth: Authentication) -> Bool func isUserAuthenticated(for auth: Authentication) -> Bool
/** /**
Reset the user's entire authentication state. Reset the service's cached authentication state for the
*/ provided authentication type.
func resetUserAuthentication()
/**
Reset the user's authentication state for a single type
of authentication.
*/ */
func resetUserAuthentication(for auth: Authentication) func resetUserAuthentication(for auth: Authentication)
/**
Reset the service's cached authentication state for all
authentication types.
*/
func resetUserAuthentications()
} }

View File

@ -10,10 +10,7 @@ import Foundation
/** /**
This service wraps another authentication service and keeps This service wraps another authentication service and keeps
authentication results in a cache. `resetUserAuthentication` authentication results in a cache.
can be called to clear one or all caches and force users to
perform new authentications to , e.g. after sending the app
to the background.
*/ */
public class CachedAuthenticationServiceProxy: CachedAuthenticationService { public class CachedAuthenticationServiceProxy: CachedAuthenticationService {
@ -34,6 +31,11 @@ public class CachedAuthenticationServiceProxy: CachedAuthenticationService {
// MARK: - Functions // MARK: - Functions
/**
Authenticate the user for a certain authentication type.
`reason` can be used to display information to the user.
*/
public func authenticateUser(for auth: Authentication, reason: String, completion: @escaping AuthCompletion) { public func authenticateUser(for auth: Authentication, reason: String, completion: @escaping AuthCompletion) {
if isUserAuthenticated(for: auth) { return completion(.success(())) } if isUserAuthenticated(for: auth) { return completion(.success(())) }
baseService.authenticateUser(for: auth, reason: reason) { result in baseService.authenticateUser(for: auth, reason: reason) { result in
@ -42,21 +44,37 @@ public class CachedAuthenticationServiceProxy: CachedAuthenticationService {
} }
} }
/**
Check if the service instance can authenticate the user
for a certain authentication type.
*/
public func canAuthenticateUser(for auth: Authentication) -> Bool { public func canAuthenticateUser(for auth: Authentication) -> Bool {
baseService.canAuthenticateUser(for: auth) baseService.canAuthenticateUser(for: auth)
} }
/**
Check if the service has already authenticated the user
for a certain authentication type.
*/
public func isUserAuthenticated(for auth: Authentication) -> Bool { public func isUserAuthenticated(for auth: Authentication) -> Bool {
cache[auth.id] ?? false cache[auth.id] ?? false
} }
public func resetUserAuthentication() { /**
cache.removeAll() Reset the service's cached authentication state for the
} provided authentication type.
*/
public func resetUserAuthentication(for auth: Authentication) { public func resetUserAuthentication(for auth: Authentication) {
setIsAuthenticated(false, for: auth) setIsAuthenticated(false, for: auth)
} }
/**
Reset the service's cached authentication state for all
authentication types.
*/
public func resetUserAuthentications() {
cache.removeAll()
}
} }

View File

@ -115,7 +115,7 @@ class CachedAuthenticationServiceProxyTests: QuickSpec {
service.authenticateUser(for: auth, reason: "") { _ in service.authenticateUser(for: auth, reason: "") { _ in
expect(service.isUserAuthenticated(for: .standard)).to(beTrue()) expect(service.isUserAuthenticated(for: .standard)).to(beTrue())
expect(service.isUserAuthenticated(for: auth)).to(beTrue()) expect(service.isUserAuthenticated(for: auth)).to(beTrue())
service.resetUserAuthentication() service.resetUserAuthentications()
expect(service.isUserAuthenticated(for: .standard)).to(beFalse()) expect(service.isUserAuthenticated(for: .standard)).to(beFalse())
expect(service.isUserAuthenticated(for: auth)).to(beFalse()) expect(service.isUserAuthenticated(for: auth)).to(beFalse())
asyncTrigger.trigger() asyncTrigger.trigger()