Swift - Locksmith loadDataForUserAccount 有时会失败?
Swift - Locksmith loadDataForUserAccount fails sometimes?
我有一个奇怪的错误,它只发生在少数用户的 iPhone 上,详情如下 -
应用使用通用框架(自研)保存keychain登录成功后的accessToken和refreshToken。我们正在使用 Locksmith 来实现功能 - 在用户注销时保存、加载数据和删除。
每次应用程序被终止并启动或 applicationWillEnterForeground
时,令牌都会在服务调用的帮助下刷新并再次保存到钥匙串中。当refreshToken
过期时(此令牌有效期为一个月),用户会收到通知,该应用程序已长时间未使用并已注销。
实际问题在于,只有少数用户即使每天使用该应用程序(即在 refreshToken
完成一个月之前)刷新机制也会失败。与后端团队核实后,refresh
服务始终可用,所以我怀疑锁匠 loadDataForUserAccount
但无法重现该问题。另外,可能用户不会遇到这个问题。一切正常,如预期。
谁能帮助我进一步了解如何确定原因?
下面是刷新accessToken
和refreshToken
的代码
** 当应用程序进入前台或终止并启动时从应用程序调用刷新令牌**
if let mySession = ServiceLayer.sharedInstance.session {
mySession.refresh { result in
switch result {
case .failure(.authenticationFailure):
if isBackgroundFetch {
print("⚠️ Session refresh failed, user is now logged out.")
self.myService.logoutCurrentUser()
// Logout Current user
mySession.invalidate()
self.showLoginUI()
}
else {
// user accessToken is invalid but provide access to QR
// on the home screen. disable all other actions except logout button
self.showHomeScreen()
}
default:
mySession.getAccessToken { result in
switch result {
case let .success(value):
print("Access Token from App Delegate \(value)")
myAccessToken = value
case let .failure(error):
print("❌ Failed to fetch AccessToken: \(error)")
}
}
}
}
}
来自实现刷新机制的框架
public func refresh(_ completion: @escaping (MyResult<String, MyError>) -> (Void)) {
guard isValid else {
completion(.failure(.invalidSession))
return
}
getRefreshToken { result in
switch result {
case let .success(refreshToken):
// Get new tokens.
ServiceClient.requestJSON(ServiceRequest.refreshToken(refreshToken: refreshToken)) { result in
switch result {
case let .success(dictionary):
var newAccessToken: String?
var newRefreshToken: String?
for (key, value) in dictionary {
if key as! String == "access_token" {
newAccessToken = value as? String
}
if key as! String == "refresh_token" {
newRefreshToken = value as? String
}
}
guard newAccessToken != nil && newRefreshToken != nil else {
completion(.failure(.general))
return
}
print("Renewed session tokens.")
do {
try Locksmith.updateData(data: [MySession.accessTokenKeychainKey: newAccessToken!, MySession.refreshTokenKeychainKey: newRefreshToken!],
forUserAccount: MySession.myKeychainAccount)
}
catch {
completion(.failure(.general))
}
completion(.success(newAccessToken!))
case let .failure(error):
if error == MyError.authenticationFailure {
print(“Session refresh failed due to authentication error; invalidating session.")
self.invalidate()
}
completion(.failure(error))
}
}
case let .failure(error):
completion(.failure(error))
}
}
}
应用程序可能在设备锁定时在后台启动(用于应用程序刷新或您配置的其他后台模式)。受保护的数据(包括钥匙串)此时不一定可用。您可以检查 UIApplication.isProtectedDataAvailable
以检查它是否可用,并且您可以将项目的保护降低到 kSecAttrAccessibleAfterFirstUnlock
以便更可靠地进行后台访问(尽管不是 100% 承诺,即使在该模式下)。锁匠称之为 AfterFirstUnlock
.
我有一个奇怪的错误,它只发生在少数用户的 iPhone 上,详情如下 -
应用使用通用框架(自研)保存keychain登录成功后的accessToken和refreshToken。我们正在使用 Locksmith 来实现功能 - 在用户注销时保存、加载数据和删除。
每次应用程序被终止并启动或 applicationWillEnterForeground
时,令牌都会在服务调用的帮助下刷新并再次保存到钥匙串中。当refreshToken
过期时(此令牌有效期为一个月),用户会收到通知,该应用程序已长时间未使用并已注销。
实际问题在于,只有少数用户即使每天使用该应用程序(即在 refreshToken
完成一个月之前)刷新机制也会失败。与后端团队核实后,refresh
服务始终可用,所以我怀疑锁匠 loadDataForUserAccount
但无法重现该问题。另外,可能用户不会遇到这个问题。一切正常,如预期。
谁能帮助我进一步了解如何确定原因?
下面是刷新accessToken
和refreshToken
** 当应用程序进入前台或终止并启动时从应用程序调用刷新令牌**
if let mySession = ServiceLayer.sharedInstance.session {
mySession.refresh { result in
switch result {
case .failure(.authenticationFailure):
if isBackgroundFetch {
print("⚠️ Session refresh failed, user is now logged out.")
self.myService.logoutCurrentUser()
// Logout Current user
mySession.invalidate()
self.showLoginUI()
}
else {
// user accessToken is invalid but provide access to QR
// on the home screen. disable all other actions except logout button
self.showHomeScreen()
}
default:
mySession.getAccessToken { result in
switch result {
case let .success(value):
print("Access Token from App Delegate \(value)")
myAccessToken = value
case let .failure(error):
print("❌ Failed to fetch AccessToken: \(error)")
}
}
}
}
}
来自实现刷新机制的框架
public func refresh(_ completion: @escaping (MyResult<String, MyError>) -> (Void)) {
guard isValid else {
completion(.failure(.invalidSession))
return
}
getRefreshToken { result in
switch result {
case let .success(refreshToken):
// Get new tokens.
ServiceClient.requestJSON(ServiceRequest.refreshToken(refreshToken: refreshToken)) { result in
switch result {
case let .success(dictionary):
var newAccessToken: String?
var newRefreshToken: String?
for (key, value) in dictionary {
if key as! String == "access_token" {
newAccessToken = value as? String
}
if key as! String == "refresh_token" {
newRefreshToken = value as? String
}
}
guard newAccessToken != nil && newRefreshToken != nil else {
completion(.failure(.general))
return
}
print("Renewed session tokens.")
do {
try Locksmith.updateData(data: [MySession.accessTokenKeychainKey: newAccessToken!, MySession.refreshTokenKeychainKey: newRefreshToken!],
forUserAccount: MySession.myKeychainAccount)
}
catch {
completion(.failure(.general))
}
completion(.success(newAccessToken!))
case let .failure(error):
if error == MyError.authenticationFailure {
print(“Session refresh failed due to authentication error; invalidating session.")
self.invalidate()
}
completion(.failure(error))
}
}
case let .failure(error):
completion(.failure(error))
}
}
}
应用程序可能在设备锁定时在后台启动(用于应用程序刷新或您配置的其他后台模式)。受保护的数据(包括钥匙串)此时不一定可用。您可以检查 UIApplication.isProtectedDataAvailable
以检查它是否可用,并且您可以将项目的保护降低到 kSecAttrAccessibleAfterFirstUnlock
以便更可靠地进行后台访问(尽管不是 100% 承诺,即使在该模式下)。锁匠称之为 AfterFirstUnlock
.