Skip to content

Commit d5b2bec

Browse files
author
Thomas Roovers
committed
Preventing authentication concurrency and queueing concurrent request
1 parent 6248fc3 commit d5b2bec

File tree

3 files changed

+38
-14
lines changed

3 files changed

+38
-14
lines changed

Sources/Core/Authentication/AuthenticationProvider.swift

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import CommonCrypto
1616
class AuthenticationProvider {
1717
private weak var client: Client!
1818
private(set) var isAuthenticating = false
19-
19+
2020
required init(client: Client) {
2121
self.client = client
2222
}
@@ -132,7 +132,7 @@ class AuthenticationProvider {
132132
throw Error.missingClientAuthentication.set(request: request)
133133
}
134134

135-
return sendOAuthRequest(grantType: grantType, parameters: parameters)
135+
return try sendOAuthRequest(grantType: grantType, parameters: parameters)
136136
.flatMap { [weak self] _ -> Single<Request> in
137137
guard let self = self else {
138138
return Single<Request>.never()
@@ -141,7 +141,15 @@ class AuthenticationProvider {
141141
}
142142
}
143143

144-
func sendOAuthRequest(grantType: OAuthenticationGrantType, parameters: Parameters? = nil) -> Single<Void> {
144+
func sendOAuthRequest(grantType: OAuthenticationGrantType, parameters: Parameters? = nil) throws -> Single<Void> {
145+
146+
if isAuthenticating {
147+
client.logger?.trace("Already authenticating, stopping the concurrent request")
148+
throw Error.concurrentAuthentication
149+
}
150+
151+
isAuthenticating = true
152+
145153
var parameters = parameters ?? [:]
146154
parameters["grant_type"] = grantType.rawValue
147155

@@ -161,16 +169,14 @@ class AuthenticationProvider {
161169
"access_token": client.config.logging.maskTokens ? .halfMasked : .default,
162170
"refresh_token": client.config.logging.maskTokens ? .halfMasked : .default,
163171
])
164-
165172
}
166173

167-
isAuthenticating = true
168-
169174
return client.request(request).flatMap { [weak self, client] json in
170175
let accessToken = try json.map(to: AccessToken.self)
171176
accessToken.host = client?.authenticationHost ?? ""
172177
accessToken.grantType = grantType
173178
accessToken.store()
179+
174180
client?.logger?.debug("Store access-token: \(optionalDescription(accessToken))")
175181
client?.authorizationGrantTypeSubject.onNext(accessToken.grantType)
176182
self?.isAuthenticating = false
@@ -189,6 +195,7 @@ class AuthenticationProvider {
189195
// So we can completely remove the access-token, since it is in no way able to revalidate.
190196
client?.logger?.warning("Clearing access-token; invalid refresh-token")
191197
client?.clearAccessToken()
198+
192199
throw Error.refreshTokenInvalidated
193200
}
194201
}
@@ -225,9 +232,7 @@ class AuthenticationProvider {
225232

226233
authURLString += "&code_challenge=\(codeChallenge)&code_challenge_method=S256"
227234
}
228-
229-
print("authUrl: \(authURLString)")
230-
235+
231236
guard let authURL = URL(string: authURLString) else {
232237
observer(.failure(Error.invalidUrl))
233238
return Disposables.create()
@@ -255,7 +260,11 @@ class AuthenticationProvider {
255260
parameters["code_verifier"] = codeVerifier
256261
}
257262

258-
return sendOAuthRequest(grantType: .authorizationCode, parameters: parameters)
263+
do {
264+
return try sendOAuthRequest(grantType: .authorizationCode, parameters: parameters)
265+
} catch {
266+
return Single<Void>.error(error)
267+
}
259268
}
260269

261270
private func _createCodeVerifier() -> String {
@@ -326,7 +335,12 @@ class AuthenticationProvider {
326335

327336
accessToken.invalidate()
328337
return client.request(request)
338+
339+
} else if error == Error.concurrentAuthentication {
340+
// Retry the request, which will probably add it to the queue because there's an authentication request pending
341+
return client.request(request)
329342
}
343+
330344
throw error
331345
}
332346
}

Sources/Core/Base/Client.swift

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ open class Client: ReactiveCompatible {
102102
///
103103
/// - Returns: `Promise<JSON>`
104104
open func request(_ request: Request) -> Single<JSON> {
105+
105106
// Strip slashes to form a valid urlString
106107
guard let host = (request.host ?? config.host) else {
107108
return Single<JSON>.error(Error.invalidRequest("Missing 'host'").set(request: request))
@@ -142,14 +143,14 @@ open class Client: ReactiveCompatible {
142143
return self._request(newRequest)
143144

144145
// 3. If for some reason an error occurs, we check with the auth-provider if we need to retry
145-
}.catch { [weak self, authProvider] error -> Single<JSON> in
146+
}.catch { [weak self, authProvider, logger] error -> Single<JSON> in
146147
self?.queue.removeFirst()
147148
return try authProvider.recover(from: error, request: request)
148149

149150
// 4. If any other requests are queued, fire up the next one
150151
}.flatMap { [queue] json -> Single<JSON> in
151152
// When a request is finished, no matter if its succesful or not
152-
// We try to clear th queue
153+
// We try to clear the queue
153154
if request.requiresOAuthentication {
154155
queue.next()
155156
}
@@ -298,7 +299,12 @@ open class Client: ReactiveCompatible {
298299
"username": username,
299300
"password": password
300301
]
301-
return authProvider.sendOAuthRequest(grantType: .password, parameters: parameters)
302+
303+
do {
304+
return try authProvider.sendOAuthRequest(grantType: .password, parameters: parameters)
305+
} catch {
306+
return Single<Void>.error(error)
307+
}
302308
}
303309

304310
open func startAuthorizationFlow(scope: [String], redirectUri: String) -> Single<AuthorizationCodeRequest> {

Sources/Core/Base/Error.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,14 @@ public class Error: Swift.Error {
5454
public static func unknown(_ json: JSON? = nil) -> Error {
5555
return Error(code: 100, json: json)
5656
}
57-
57+
5858
public static func invalidRequest(_ message: String) -> Error {
5959
return Error(code: 301, message: message)
6060
}
61+
62+
public static var concurrentAuthentication: Error {
63+
return Error(code: 401, message: "Concurrent authentication requests")
64+
}
6165

6266
public static func underlying(_ error: Swift.Error, json: JSON? = nil) -> Error {
6367
let apiError = Error(code: 601)

0 commit comments

Comments
 (0)