Skip to content

Commit fa9b2de

Browse files
KAPE-1648 Properly delete API and VPN token when they fail to refresh or when user logs out
KM-13631 Add extra logs
1 parent 60c6090 commit fa9b2de

5 files changed

Lines changed: 69 additions & 27 deletions

File tree

LocalPackages/PIALibrary/Sources/PIALibrary/Account/Data/ClientErrorMapper.swift

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
2-
31
import Foundation
42

3+
private let log = PIALogger.logger(for: ClientErrorMapper.self)
4+
55
/// Maps an Network Error with a ClientError
66
/// The idea is to use this mapper on `PIAWebServices` to map the errors returned from the Swift implementation of the Accounts Lib with the ones that the app expects
77
struct ClientErrorMapper {
88
static func map(networkRequestError: NetworkRequestError) -> ClientError {
9+
log.info("Will map ClientError from NetworkRequestError: \(networkRequestError)")
10+
911
switch networkRequestError {
1012
case .connectionError(let statusCode, let message):
13+
log.info(".connectionError - statusCode: \(statusCode?.description ?? "nil"), message: \(message ?? "nil")")
1114
return getClientError(from: statusCode) ?? .unexpectedReply
1215

1316
case .allConnectionAttemptsFailed(let statusCode):
@@ -29,6 +32,7 @@ struct ClientErrorMapper {
2932
return .malformedResponseData
3033

3134
case .unknown(message: let message):
35+
log.info(".unknown - message: \(message ?? "nil")")
3236
return .unexpectedReply
3337

3438
case .unableToDecodeAPIToken, .unableToDecodeDataContent:
@@ -49,17 +53,25 @@ struct ClientErrorMapper {
4953
}
5054

5155
static func getClientError(from statusCode: Int?) -> ClientError? {
52-
53-
guard let statusCode,
54-
let httpStatusCode = HttpResponseStatusCode(rawValue: statusCode) else {
56+
guard
57+
let statusCode,
58+
let httpStatusCode = HttpResponseStatusCode(rawValue: statusCode)
59+
else {
60+
log.error("Unable to map ClientError due to lack of statusCode")
5561
return nil
5662
}
63+
5764
switch httpStatusCode {
5865
case .unauthorized:
66+
log.info("Did map \(ClientError.unauthorized) out of httpStatusCode: \(httpStatusCode)")
5967
return .unauthorized
68+
6069
case .throttled:
70+
log.info("Did map \(ClientError.throttled(retryAfter: 60)) out of httpStatusCode: \(httpStatusCode)")
6171
return .throttled(retryAfter: 60)
72+
6273
default:
74+
log.info("Mapped no ClientError out of httpStatusCode: \(httpStatusCode)")
6375
return nil
6476
}
6577
}

LocalPackages/PIALibrary/Sources/PIALibrary/Account/DefaultAccountProvider.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,9 @@ open class DefaultAccountProvider: AccountProvider, ConfigurationAccess, Databas
601601
accessedDatabase.plain.orderedTiles = AvailableTiles.defaultTiles()
602602
accessedDatabase.plain.historicalServers = []
603603
accessedDatabase.plain.reset()
604+
605+
apiTokenProvider.clearAPIToken()
606+
vpnTokenProvider.clearVpnToken()
604607
}
605608

606609
#endif

LocalPackages/PIALibrary/Sources/PIALibrary/Account/Domain/UseCases/RefreshAPITokenUseCase.swift

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import Foundation
33
import NWHttpConnection
44

5+
private let log = PIALogger.logger(for: RefreshAPITokenUseCase.self)
6+
57
protocol RefreshAPITokenUseCaseType {
68
typealias Completion = ((NetworkRequestError?) -> Void)
79
func callAsFunction(completion: @escaping RefreshAPITokenUseCaseType.Completion)
@@ -18,30 +20,35 @@ class RefreshAPITokenUseCase: RefreshAPITokenUseCaseType {
1820
}
1921

2022
func callAsFunction(completion: @escaping RefreshAPITokenUseCaseType.Completion) {
21-
2223
let configuration = RefreshApiTokenRequestConfiguration()
2324

2425
networkClient.executeRequest(with: configuration) { [weak self] error, dataResponse in
25-
guard let self else { return }
26-
2726
if let error {
27+
// Clear old token on refresh failure to prevent repeated auth failures
28+
log.info("Clearing API token due to error: \(error)")
29+
self?.apiTokenProvider.clearAPIToken()
2830
completion(error)
2931
} else if let dataResponse {
30-
self.handleDataResponse(dataResponse, completion: completion)
32+
self?.handleDataResponse(dataResponse, completion: completion)
3133
} else {
34+
// Clear old token on refresh failure to prevent repeated auth failures
35+
log.info("Clearing API token due to allConnectionAttemptsFailed (no error and no response)")
36+
self?.apiTokenProvider.clearAPIToken()
3237
completion(NetworkRequestError.allConnectionAttemptsFailed())
3338
}
3439
}
35-
3640
}
3741

3842
}
3943

4044

4145
private extension RefreshAPITokenUseCase {
42-
private func handleDataResponse(_ dataResponse: NetworkRequestResponseType, completion: @escaping RefreshVpnTokenUseCaseType.Completion) {
43-
46+
47+
private func handleDataResponse(_ dataResponse: NetworkRequestResponseType, completion: @escaping RefreshAPITokenUseCaseType.Completion) {
4448
guard let dataResponseContent = dataResponse.data else {
49+
// Clear old token on refresh failure to prevent repeated auth failures
50+
log.info("Clearing API token due to noDataContent in response")
51+
apiTokenProvider.clearAPIToken()
4552
completion(NetworkRequestError.noDataContent)
4653
return
4754
}
@@ -50,9 +57,11 @@ private extension RefreshAPITokenUseCase {
5057
try apiTokenProvider.saveAPIToken(from: dataResponseContent)
5158
completion(nil)
5259
} catch {
60+
// Clear old token when unable to save new one to prevent repeated auth failures
61+
log.info("Clearing API token due to save failure - error: \(error)")
62+
apiTokenProvider.clearAPIToken()
5363
completion(NetworkRequestError.unableToSaveAPIToken)
5464
}
55-
5665
}
5766

5867
}

LocalPackages/PIALibrary/Sources/PIALibrary/Account/Domain/UseCases/RefreshVpnTokenUseCase.swift

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11

22
import Foundation
33

4+
private let log = PIALogger.logger(for: RefreshVpnTokenUseCase.self)
5+
46
protocol RefreshVpnTokenUseCaseType {
57
typealias Completion = ((NetworkRequestError?) -> Void)
68
func callAsFunction(completion: @escaping RefreshVpnTokenUseCaseType.Completion)
@@ -17,30 +19,35 @@ class RefreshVpnTokenUseCase: RefreshVpnTokenUseCaseType {
1719
}
1820

1921
func callAsFunction(completion: @escaping RefreshVpnTokenUseCaseType.Completion) {
20-
2122
let configuration = RefreshVpnTokenRequestConfiguration()
2223

2324
networkClient.executeRequest(with: configuration) { [weak self] error, dataResponse in
24-
guard let self else { return }
25-
2625
if let error {
26+
// Clear old token on refresh failure to prevent repeated auth failures
27+
log.info("Clearing VPN token due to error: \(error)")
28+
self?.vpnTokenProvider.clearVpnToken()
2729
completion(error)
2830
} else if let dataResponse {
29-
self.handleDataResponse(dataResponse, completion: completion)
31+
self?.handleDataResponse(dataResponse, completion: completion)
3032
} else {
33+
// Clear old token on refresh failure to prevent repeated auth failures
34+
log.info("Clearing VPN token due to allConnectionAttemptsFailed (no error and no response)")
35+
self?.vpnTokenProvider.clearVpnToken()
3136
completion(NetworkRequestError.allConnectionAttemptsFailed())
3237
}
33-
3438
}
3539
}
36-
40+
3741
}
3842

3943

4044
private extension RefreshVpnTokenUseCase {
45+
4146
private func handleDataResponse(_ dataResponse: NetworkRequestResponseType, completion: @escaping RefreshVpnTokenUseCaseType.Completion) {
42-
4347
guard let dataResponseContent = dataResponse.data else {
48+
// Clear old token on refresh failure to prevent repeated auth failures
49+
log.info("Clearing VPN token due to noDataContent in response")
50+
vpnTokenProvider.clearVpnToken()
4451
completion(NetworkRequestError.noDataContent)
4552
return
4653
}
@@ -49,9 +56,10 @@ private extension RefreshVpnTokenUseCase {
4956
try vpnTokenProvider.saveVpnToken(from: dataResponseContent)
5057
completion(nil)
5158
} catch {
59+
// Clear old token when unable to save new one to prevent repeated auth failures
60+
log.info("Clearing VPN token due to save failure - error: \(error)")
61+
vpnTokenProvider.clearVpnToken()
5262
completion(NetworkRequestError.unableToSaveVpnToken)
5363
}
54-
5564
}
56-
5765
}

LocalPackages/PIALibrary/Sources/PIALibrary/Common/Data/Networking/NetworkRequestClient.swift

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import Foundation
33
import NWHttpConnection
44

5+
private let log = PIALogger.logger(for: NetworkRequestClient.self)
56

67
protocol NetworkRequestClientType {
78
typealias Completion = ((NetworkRequestError?, NetworkRequestResponseType?) -> Void)
@@ -42,8 +43,9 @@ private extension NetworkRequestClient {
4243

4344
/// Serial execution of all the connections until one succeeds or completes with an error when all connection attempts fail
4445
func executeRecursivelyUntilSuccess(connections: [NWHttpConnectionType], completion: @escaping NetworkRequestClientType.Completion) {
45-
46+
4647
guard !connections.isEmpty else {
48+
log.error("All connection attempts failed: No connections available")
4749
completion(.allConnectionAttemptsFailed(statusCode: nil), nil)
4850
return
4951
}
@@ -54,6 +56,7 @@ private extension NetworkRequestClient {
5456
func tryNextConnectionOrFail(currentStatusCode: Int?) {
5557
if remainingConnections.isEmpty {
5658
// No more endpoints to try a connection
59+
log.error("All connection attempts failed: No more endpoints to try (statusCode: \(currentStatusCode?.description ?? "nil"))")
5760
let requestError = NetworkRequestError.allConnectionAttemptsFailed(statusCode: currentStatusCode)
5861
completion(requestError, nil)
5962
} else {
@@ -64,20 +67,23 @@ private extension NetworkRequestClient {
6467

6568
execute(connection: nextConnection) { error, responseData in
6669

67-
if error != nil {
70+
if let error = error {
71+
log.error("Connection error: \(error) (statusCode: \(responseData?.statusCode?.description ?? "nil"))")
6872
tryNextConnectionOrFail(currentStatusCode: responseData?.statusCode)
6973
} else if let responseData {
7074
let statusCode: Int = responseData.statusCode ?? -1
7175
let isSuccessStatusCode = statusCode > 199 && statusCode < 300
72-
76+
7377
if isSuccessStatusCode {
7478
completion(nil, responseData)
7579
} else {
7680
// Connection did not succeed, try the next one
81+
log.error("Non-success status code received: \(statusCode)")
7782
tryNextConnectionOrFail(currentStatusCode: responseData.statusCode)
7883
}
7984
} else {
8085
// No error and no data
86+
log.error("Connection completed with no error and no data")
8187
tryNextConnectionOrFail(currentStatusCode: nil)
8288
}
8389
}
@@ -88,25 +94,29 @@ private extension NetworkRequestClient {
8894
func execute(connection: NWHttpConnectionType, completion: @escaping NetworkRequestClientType.Completion) {
8995
do {
9096
var connectionHandled: Bool = false
91-
97+
9298
try connection.connect { error, dataResponse in
9399
if let error {
94100
connectionHandled = true
101+
log.error("Connection error in single connection: \(error.localizedDescription) (statusCode: \(dataResponse?.statusCode?.description ?? "nil"))")
95102
completion(NetworkRequestError.connectionError(statusCode: dataResponse?.statusCode, message: error.localizedDescription), nil)
96103
} else if let dataResponse = dataResponse as? NetworkRequestResponseType {
97104
connectionHandled = true
98105
completion(nil, dataResponse)
99106
} else {
100107
connectionHandled = true
108+
log.error("No error and no response in single connection")
101109
completion(NetworkRequestError.noErrorAndNoResponse, nil)
102110
}
103111
} completion: {
104112
if connectionHandled == false {
113+
log.error("Connection completed with no response")
105114
completion(NetworkRequestError.connectionCompletedWithNoResponse, nil)
106115
}
107116
}
108-
117+
109118
} catch {
119+
log.error("Unknown error in connection execution: \(error.localizedDescription)")
110120
completion(NetworkRequestError.unknown(message: error.localizedDescription), nil)
111121
}
112122
}

0 commit comments

Comments
 (0)