Lashtot
Lashtot

Reputation: 61

Alamofire 5, How to stop all requests, refresh access token and rerun all stopped requests?

Situation I need to fix - Imagine 2 requests are made in same time (when access token is not valid). Both try to refresh token but one of them will invalidate token for another one.

Is there any way how to:

  1. allow only 1 to refresh token
  2. stop all other requests
  3. rerun all stopped requests (when token is refreshed)

Or do you have any idea how to solve this by other way?

This is how my request look like in every view controller:

AF.request(encodedURLRequest, interceptor: AuthInterceptor()).validate().responseData { (response) in
    ...
}

This is my AuthInterceptor:

final class AuthInterceptor: RequestInterceptor {

    func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
        var adaptedUrlRequest = urlRequest
        adaptedUrlRequest.setValue("Bearer \(UserDefaults.standard.getAccessToken())", forHTTPHeaderField: "Authorization")
        completion(.success(adaptedUrlRequest))
    }

    func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) {
        print("request \(request) failed")
        if let response = request.task?.response as? HTTPURLResponse, response.statusCode == 403 {
            guard let url = URL(string: Endpoint.login.url) else { return }
            var urlRequest = URLRequest(url: url)
            urlRequest.httpMethod = "POST"
            urlRequest.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")

            let parameters: [String: String] = [
                "refresh_token": UserDefaults.standard.getRefreshToken(),
                "grant_type": "refresh_token"
            ]

            guard let encodedURLRequest = try? URLEncodedFormParameterEncoder.default.encode(parameters,
                                                                                             into: urlRequest) else { return }

            AF.request(encodedURLRequest).validate().responseData { (response) in
                if let data = response.data {
                    let decoder = JSONDecoder()
                    decoder.keyDecodingStrategy = .convertFromSnakeCase
                    if let loginResponse = try? decoder.decode(LoginResponse.self, from: data) {
                        UserDefaults.standard.setAccessToken(value: loginResponse.accessToken)
                        UserDefaults.standard.setRefreshToken(value: loginResponse.refreshToken)
                        completion(.retryWithDelay(1))
                    }
                }
            }
        }
    }

}

Upvotes: 2

Views: 1615

Answers (1)

Jon Shier
Jon Shier

Reputation: 12770

You can use Alamofire's RequestRetrier protocol (as part of the RequestInterceptor protocol) to do this without having to manually start and stop requests.

Essentially, you need to know when a refresh is being performed and store any additional completion handlers from requests which failed because of the expired token. You can do this in your AuthInterceptor class. Just make sure your implementation is thread safe!

Upvotes: 1

Related Questions