mirror of
https://github.com/AU-COVIDSafe/mobile-ios.git
synced 2025-04-05 14:24:59 +00:00
110 lines
4.3 KiB
Swift
110 lines
4.3 KiB
Swift
//
|
|
// CovidRequestInterceptor.swift
|
|
// CovidSafe
|
|
//
|
|
// Copyright © 2020 Australian Government. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
import Alamofire
|
|
import KeychainSwift
|
|
|
|
final class CovidRequestRetrier: Alamofire.RequestInterceptor {
|
|
private let numRetries: Int
|
|
private var retriesExecuted: Int = 0
|
|
private var triedRefresh = false
|
|
|
|
init(retries: Int) {
|
|
self.numRetries = retries
|
|
}
|
|
|
|
func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
|
|
var urlRequest = urlRequest
|
|
let keychain = KeychainSwift.shared
|
|
let refreshExists = keychain.get("REFRESH_TOKEN") != nil
|
|
|
|
// turn off geolock error
|
|
UserDefaults.standard.setValue(false, forKey: showGeolockErrorKey)
|
|
|
|
// prevent authenticated api calls if the re-registration flow has been started
|
|
if UserDefaults.standard.bool(forKey: "ReauthenticationNeededKey") &&
|
|
refreshExists {
|
|
completion(.failure(CovidSafeAPIError.TokenExpiredError))
|
|
return
|
|
}
|
|
|
|
// check headers an update if needed.
|
|
// intercept the first call to the API after app updates to retrieve new tokens
|
|
if !refreshExists &&
|
|
keychain.get("JWT_TOKEN") != nil {
|
|
AuthenticationAPI.issueTokensAPI { (response, error) in
|
|
guard let token = response?.token else {
|
|
completion(.success(urlRequest))
|
|
return
|
|
}
|
|
// update the token
|
|
urlRequest.headers.add(name: "Authorization", value: "Bearer \(token)")
|
|
completion(.success(urlRequest))
|
|
}
|
|
return
|
|
}
|
|
|
|
guard let token = keychain.get("JWT_TOKEN"),
|
|
urlRequest.headers["Authorization"] != nil else {
|
|
completion(.success(urlRequest))
|
|
return
|
|
}
|
|
|
|
// update the token in case is was updated in a retry
|
|
urlRequest.headers.add(name: "Authorization", value: "Bearer \(token)")
|
|
completion(.success(urlRequest))
|
|
}
|
|
|
|
func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) {
|
|
|
|
guard retriesExecuted < numRetries else {
|
|
completion(.doNotRetryWithError(error))
|
|
return
|
|
}
|
|
|
|
if let covidError = error.asAFError?.underlyingError as? CovidSafeAPIError, covidError == .TokenExpiredError {
|
|
retriesExecuted = numRetries
|
|
// for some reason the retry is getting called even after doNotRetryWithError below.
|
|
// set retries to max and the guard above stops it all
|
|
completion(.doNotRetryWithError(error))
|
|
return
|
|
}
|
|
|
|
guard let response = request.task?.response as? HTTPURLResponse, response.statusCode == 403 || response.statusCode == 401 else {
|
|
/// The request did not fail due to a 403 Forbidden response.
|
|
let isServerTrustEvaluationError = error.asAFError?.isServerTrustEvaluationError ?? false
|
|
if ( retriesExecuted >= numRetries || isServerTrustEvaluationError) {
|
|
return completion(.doNotRetryWithError(error))
|
|
}
|
|
retriesExecuted += 1
|
|
return completion(.retryWithDelay(1.0))
|
|
}
|
|
|
|
if let serverHeader = response.headers.first(where: { $0.name == "Server" }),
|
|
response.statusCode == 403 && serverHeader.value == "CloudFront" {
|
|
UserDefaults.standard.setValue(true, forKey: showGeolockErrorKey)
|
|
return completion(.doNotRetryWithError(error))
|
|
}
|
|
|
|
if !triedRefresh &&
|
|
(response.statusCode == 401 || response.statusCode == 403) {
|
|
triedRefresh = true
|
|
retriesExecuted += 1
|
|
AuthenticationAPI.issueTokensAPI { (response, authError) in
|
|
// this will update the tokens automatically
|
|
if let respError = authError, respError == .TokenExpiredError {
|
|
completion(.doNotRetryWithError(error))
|
|
return
|
|
}
|
|
completion(.retryWithDelay(1.0))
|
|
}
|
|
return
|
|
}
|
|
return completion(.doNotRetryWithError(error))
|
|
}
|
|
}
|