-
Notifications
You must be signed in to change notification settings - Fork 0
/
Authentication.swift
156 lines (139 loc) · 5.24 KB
/
Authentication.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
//
// Authentication.swift
// HyperClient
//
// Created by Niko Ma on 3/28/22.
//
import SwiftUI
import LocalAuthentication
struct Credentials: Codable {
var username: String = ""
var password: String = ""
}
class Authentication: ObservableObject {
@Published var credentials = Credentials()
@Published var isValidated = false
@Published var isAuthorized = false
@Published var storeCredentialsNextTime = false
@Published var error: Authentication.AuthenticationError?
enum BiometricType {
case none
case face
case touch
}
enum AuthenticationError: Error, LocalizedError, Identifiable {
case invalidCredentials
case deniedAccess
case noFaceIdEnrolled
case noFingerprintEnrolled
case biometrictError
case credentialsNotSaved
var id: String {
self.localizedDescription
}
var errorDescription: String? {
switch self {
case .invalidCredentials:
return NSLocalizedString("账号密码错误,请重新输入", comment: "")
case .deniedAccess:
return NSLocalizedString("请在设置中打开本app的Face ID权限", comment: "")
case .noFaceIdEnrolled:
return NSLocalizedString("您还没有录入任何Face ID", comment: "")
case .noFingerprintEnrolled:
return NSLocalizedString("您还没有录入任何Touch ID", comment: "")
case .biometrictError:
return NSLocalizedString("无法识别您", comment: "")
case .credentialsNotSaved:
return NSLocalizedString("您希望保存账号密码以便下次登录吗?", comment: "")
}
}
}
func login(completion: @escaping (Bool) -> Void) {
HyperClient.shared.login(credentials: credentials) { [unowned self] result in
switch result {
case .success:
if storeCredentialsNextTime {
do {
let data = try JSONEncoder().encode(credentials)
UserDefaults.standard.set(data, forKey: "Default")
} catch {
print("UserDefaults set failed: \(error.localizedDescription)")
}
storeCredentialsNextTime = false
}
completion(true)
case .failure(let authError):
credentials = Credentials()
error = authError
completion(false)
}
}
}
func updateValidation(success: Bool) {
withAnimation {
isValidated = success
}
}
func biometricType() -> BiometricType {
let authContext = LAContext()
let _ = authContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil)
switch authContext.biometryType {
case .none:
return .none
case .touchID:
return .touch
case .faceID:
return .face
@unknown default:
return .none
}
}
func requestBiometricUnlock(completion: @escaping (Result<Credentials, AuthenticationError>) -> Void) {
var credentials: Credentials? = nil
if let data = UserDefaults.standard.data(forKey: "Default") {
do {
credentials = try JSONDecoder().decode(Credentials.self, from: data)
} catch {
print("Decode Userdefaults error: \(error.localizedDescription)")
completion(.failure(.credentialsNotSaved))
return
}
}
guard let credentials = credentials else {
completion(.failure(.credentialsNotSaved))
return
}
let context = LAContext()
var error: NSError?
let canEvaluate = context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error)
if let error = error {
switch error.code {
case -6:
completion(.failure(.deniedAccess))
case -7:
if context.biometryType == .faceID {
completion(.failure(.noFaceIdEnrolled))
} else {
completion(.failure(.noFingerprintEnrolled))
}
default:
completion(.failure(.biometrictError))
}
return
}
// Determine whether it can recognize identity
if canEvaluate {
if context.biometryType != .none {
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "需要读取登录信息") { success, error in
DispatchQueue.main.async {
if error != nil {
completion(.failure(.biometrictError))
} else {
completion(.success(credentials))
}
}
}
}
}
}
}