I am using https://github.com/p2/OAuth2 for connecting to the backend of my app via OAuth2 which works quite well.
The problem I have is when the access token expires and multiple requests happen at the same time some of them fail.
Parallel requests can be triggered from different parts of the app. For example when the app is launched the current location is sent to the server and a list of events is downloaded.
What would be the best way to make sure that no second refresh token request is made while the first is still running?
Refresh tokens never expire, unless revoked by the user. You should store it safely and permanently. You should definitely not go back and get new refresh tokens over and over, because only a certain number can be understanding per user/app combination, and eventually the older ones will stop working.
Step 1 − First, the client authenticates with the authorization server by giving the authorization grant. Step 2 − Next, the authorization server authenticates the client, validates the authorization grant and issues the access token and refresh token to the client, if valid.
Once they expire, client applications can use a refresh token to "refresh" the access token. That is, a refresh token is a credential artifact that lets a client application get new access tokens without having to ask the user to log in again.
You can get refresh tokens only for the OAuth 2.0: Authorization code flow.
Find your token lifetime and set buffer e.g 1-2 min and If your token needs refresh, save all requests when token is refreshing. After that execute all saved all requests. You can do this with DispatchQueue and DispatchWorkItem.
Example code below.
final class Network: NSObject {
static let shared = Network()
private enum Constants {
static let tokenRefreshDiffrenceMinute = 1
static let tokenExpireDateKey = "tokenExpireDate"
}
private(set) var tokenExpireDate: Date! {
didSet {
UserDefaults.standard.set(tokenExpireDate, forKey: Constants.tokenExpireDateKey)
}
}
public override init() {
super.init()
if let date = UserDefaults.standard.object(forKey: Constants.tokenExpireDateKey) as? Date {
tokenExpireDate = date
print("Token found!")
}
else {
print("Token not found!")
isTokenRefreshing = true
getToken {
self.isTokenRefreshing = false
self.executeAllSavedRequests()
}
}
}
private var isTokenRefreshing = false
private var savedRequests: [DispatchWorkItem] = []
func request(url: String, params: [String: Any], result: @escaping (String?, Error?) -> Void) {
// isTokenRefreshing save all requests
if isTokenRefreshing {
saveRequest {
self.request(url: url, params: params, result: result)
}
return
}
// if token expire
if getMinutesFrom2Dates(Date(), tokenExpireDate) < Constants.tokenRefreshDiffrenceMinute {
// open this flag for we need wait refresh token
isTokenRefreshing = true
// save current request too
saveRequest {
self.request(url: url, params: params, result: result)
}
// get token
self.getToken { [unowned self] in
self.isTokenRefreshing = false
self.executeAllSavedRequests()
}
} else {
//Alamofire.request ...
DispatchQueue.global().asyncAfter(deadline: .now() + 2) {
DispatchQueue.main.async(execute: {
result(url, nil)
})
}
}
}
private func saveRequest(_ block: @escaping () -> Void) {
// Save request to DispatchWorkItem array
savedRequests.append( DispatchWorkItem {
block()
})
}
private func executeAllSavedRequests() {
savedRequests.forEach({ DispatchQueue.global().async(execute: $0) })
savedRequests.removeAll()
}
private func getToken(completion: @escaping () -> Void) {
print("Token needs a be refresh")
// Goto server and update token
DispatchQueue.global().asyncAfter(deadline: .now() + 1) { [unowned self] in
DispatchQueue.main.async(execute: { [unowned self] in
self.tokenExpireDate = Date().addingTimeInterval(120)
print("Token refreshed!")
completion()
})
}
}
private func getMinutesFrom2Dates(_ date1: Date, _ date2: Date) -> Int {
return Calendar.current.dateComponents([.minute], from: date1, to: date2).minute!
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With