Skip to content

Commit 860135f

Browse files
committed
Make the „response provider“ generic for the returned data type
1 parent 07b4e1e commit 860135f

File tree

4 files changed

+68
-58
lines changed

4 files changed

+68
-58
lines changed

Sources/OAuthenticator/Authenticator.swift

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public enum AuthenticatorError: Error {
1818
}
1919

2020
/// Manage state required to executed authenticated URLRequests.
21-
public final class Authenticator {
21+
public final class Authenticator<UserDataType: Sendable> {
2222
public typealias UserAuthenticator = (URL, String) async throws -> URL
2323
public typealias AuthenticationStatusHandler = (Result<Login, AuthenticatorError>) -> Void
2424

@@ -81,31 +81,32 @@ public final class Authenticator {
8181

8282
let config: Configuration
8383

84-
let urlLoader: URLResponseProvider
84+
let responseLoader: URLResponseProvider
85+
let userDataLoader: URLUserDataProvider<UserDataType>
8586
private var activeTokenTask: Task<Login, Error>?
8687
private var localLogin: Login?
8788

88-
public init(config: Configuration, urlLoader loader: URLResponseProvider? = nil) {
89+
public init(config: Configuration, responseLoader: URLResponseProvider? = nil, userDataLoader: @escaping URLUserDataProvider<UserDataType>) {
8990
self.config = config
9091

91-
self.urlLoader = loader ?? URLSession.defaultProvider
92+
self.responseLoader = responseLoader ?? URLSession.defaultProvider
93+
self.userDataLoader = userDataLoader
9294
}
9395

94-
/// A default `URLSession`-backed `URLResponseProvider`.
95-
@MainActor
96-
public static let defaultResponseProvider: URLResponseProvider = {
97-
let session = URLSession(configuration: .default)
96+
public init(config: Configuration, urlLoader: URLResponseProvider? = nil) where UserDataType == Data {
97+
self.config = config
9898

99-
return session.responseProvider
100-
}()
99+
self.responseLoader = urlLoader ?? URLSession.defaultProvider
100+
self.userDataLoader = urlLoader ?? URLSession.defaultProvider
101+
}
101102

102103
/// Add authentication for `request`, execute it, and return its result.
103-
public func response(for request: URLRequest) async throws -> (Data, URLResponse) {
104+
public func response(for request: URLRequest) async throws -> (UserDataType, URLResponse) {
104105
let userAuthenticator = config.userAuthenticator
105106

106107
let login = try await loginTaskResult(manual: false, userAuthenticator: userAuthenticator)
107108

108-
let result = try await authedResponse(for: request, login: login)
109+
let result: (UserDataType, URLResponse) = try await authedResponse(for: request, login: login)
109110

110111
let action = try config.tokenHandling.responseStatusProvider(result)
111112

@@ -141,13 +142,13 @@ public final class Authenticator {
141142
}
142143
}
143144

144-
private func authedResponse(for request: URLRequest, login: Login) async throws -> (Data, URLResponse) {
145+
private func authedResponse(for request: URLRequest, login: Login) async throws -> (UserDataType, URLResponse) {
145146
var authedRequest = request
146147
let token = login.accessToken.value
147148

148149
authedRequest.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
149150

150-
return try await urlLoader(authedRequest)
151+
return try await userDataLoader(authedRequest)
151152
}
152153

153154
/// Manually perform user authentication, if required.
@@ -157,6 +158,14 @@ public final class Authenticator {
157158
}
158159
}
159160

161+
/// A default `URLSession`-backed `URLResponseProvider`.
162+
@MainActor
163+
public let defaultAuthenticatorResponseProvider: URLResponseProvider = {
164+
let session = URLSession(configuration: .default)
165+
166+
return session.responseProvider
167+
}()
168+
160169
extension Authenticator {
161170
private func retrieveLogin() async throws -> Login? {
162171
guard let storage = config.loginStorage else {
@@ -252,7 +261,7 @@ extension Authenticator {
252261
let scheme = try config.appCredentials.callbackURLScheme
253262

254263
let url = try await userAuthenticator(codeURL, scheme)
255-
let login = try await config.tokenHandling.loginProvider(url, config.appCredentials, codeURL, urlLoader)
264+
let login = try await config.tokenHandling.loginProvider(url, config.appCredentials, codeURL, responseLoader)
256265

257266
try await storeLogin(login)
258267

@@ -272,7 +281,7 @@ extension Authenticator {
272281
return nil
273282
}
274283

275-
let login = try await refreshProvider(login, config.appCredentials, urlLoader)
284+
let login = try await refreshProvider(login, config.appCredentials, responseLoader)
276285

277286
try await storeLogin(login)
278287

@@ -281,7 +290,7 @@ extension Authenticator {
281290
}
282291

283292
extension Authenticator {
284-
public var responseProvider: URLResponseProvider {
293+
public var responseProvider: URLUserDataProvider<UserDataType> {
285294
{ try await self.response(for: $0) }
286295
}
287296
}

Sources/OAuthenticator/Models.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import Foundation
55
/// This is used to abstract the actual networking system from the underlying authentication
66
/// mechanism.
77
public typealias URLResponseProvider = (URLRequest) async throws -> (Data, URLResponse)
8+
public typealias URLUserDataProvider<UserDataType: Sendable> = (URLRequest) async throws -> (UserDataType, URLResponse)
89

910
public struct Token: Codable, Hashable, Sendable {
1011
public let value: String
@@ -97,7 +98,7 @@ public struct TokenHandling {
9798
public typealias AuthorizationURLProvider = (AppCredentials) throws -> URL
9899
public typealias LoginProvider = (URL, AppCredentials, URL, URLResponseProvider) async throws -> Login
99100
public typealias RefreshProvider = (Login, AppCredentials, URLResponseProvider) async throws -> Login
100-
public typealias ResponseStatusProvider = ((Data, URLResponse)) throws -> ResponseStatus
101+
public typealias ResponseStatusProvider = ((any Sendable, URLResponse)) throws -> ResponseStatus
101102

102103
public let authorizationURLProvider: AuthorizationURLProvider
103104
public let loginProvider: LoginProvider
@@ -114,11 +115,11 @@ public struct TokenHandling {
114115
self.responseStatusProvider = responseStatusProvider
115116
}
116117

117-
public static func allResponsesValid(result: (Data, URLResponse)) throws -> ResponseStatus {
118+
public static func allResponsesValid<UserDataType: Sendable>(result: (UserDataType, URLResponse)) throws -> ResponseStatus {
118119
return .valid
119120
}
120121

121-
public static func refreshOrAuthorizeWhenUnauthorized(result: (Data, URLResponse)) throws -> ResponseStatus {
122+
public static func refreshOrAuthorizeWhenUnauthorized<UserDataType: Sendable>(result: (UserDataType, URLResponse)) throws -> ResponseStatus {
122123
guard let response = result.1 as? HTTPURLResponse else {
123124
throw AuthenticatorError.httpResponseExpected
124125
}

Tests/OAuthenticatorTests/AuthenticatorTests.swift

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,10 @@ final class AuthenticatorTests: XCTestCase {
9696
storeTokenExp.fulfill()
9797
}
9898

99-
let config = Authenticator.Configuration(appCredentials: Self.mockCredentials,
100-
loginStorage: storage,
101-
tokenHandling: tokenHandling,
102-
userAuthenticator: mockUserAuthenticator)
99+
let config = Authenticator<Data>.Configuration(appCredentials: Self.mockCredentials,
100+
loginStorage: storage,
101+
tokenHandling: tokenHandling,
102+
userAuthenticator: mockUserAuthenticator)
103103

104104
let auth = Authenticator(config: config, urlLoader: mockLoader)
105105

@@ -132,10 +132,10 @@ final class AuthenticatorTests: XCTestCase {
132132
XCTFail()
133133
}
134134

135-
let config = Authenticator.Configuration(appCredentials: Self.mockCredentials,
136-
loginStorage: storage,
137-
tokenHandling: tokenHandling,
138-
userAuthenticator: Self.disabledUserAuthenticator)
135+
let config = Authenticator<Data>.Configuration(appCredentials: Self.mockCredentials,
136+
loginStorage: storage,
137+
tokenHandling: tokenHandling,
138+
userAuthenticator: Self.disabledUserAuthenticator)
139139

140140
let auth = Authenticator(config: config, urlLoader: mockLoader)
141141

@@ -184,10 +184,10 @@ final class AuthenticatorTests: XCTestCase {
184184
XCTAssertEqual(login.accessToken.value, "REFRESHED")
185185
}
186186

187-
let config = Authenticator.Configuration(appCredentials: Self.mockCredentials,
188-
loginStorage: storage,
189-
tokenHandling: tokenHandling,
190-
userAuthenticator: Self.disabledUserAuthenticator)
187+
let config = Authenticator<Data>.Configuration(appCredentials: Self.mockCredentials,
188+
loginStorage: storage,
189+
tokenHandling: tokenHandling,
190+
userAuthenticator: Self.disabledUserAuthenticator)
191191

192192
let auth = Authenticator(config: config, urlLoader: mockLoader)
193193

@@ -219,10 +219,10 @@ final class AuthenticatorTests: XCTestCase {
219219
return URL(string: "my://login")!
220220
}
221221

222-
let config = Authenticator.Configuration(appCredentials: Self.mockCredentials,
223-
tokenHandling: tokenHandling,
224-
mode: .manualOnly,
225-
userAuthenticator: mockUserAuthenticator)
222+
let config = Authenticator<Data>.Configuration(appCredentials: Self.mockCredentials,
223+
tokenHandling: tokenHandling,
224+
mode: .manualOnly,
225+
userAuthenticator: mockUserAuthenticator)
226226

227227
let loadExp = expectation(description: "load url")
228228
let mockLoader: URLResponseProvider = { request in
@@ -286,11 +286,11 @@ final class AuthenticatorTests: XCTestCase {
286286
}
287287

288288
// Configure Authenticator with result callback
289-
let config = Authenticator.Configuration(appCredentials: Self.mockCredentials,
290-
tokenHandling: tokenHandling,
291-
mode: .manualOnly,
292-
userAuthenticator: mockUserAuthenticator,
293-
authenticationStatusHandler: authenticationCallback)
289+
let config = Authenticator<Data>.Configuration(appCredentials: Self.mockCredentials,
290+
tokenHandling: tokenHandling,
291+
mode: .manualOnly,
292+
userAuthenticator: mockUserAuthenticator,
293+
authenticationStatusHandler: authenticationCallback)
294294

295295
let loadExp = expectation(description: "load url")
296296
let mockLoader: URLResponseProvider = { request in
@@ -343,11 +343,11 @@ final class AuthenticatorTests: XCTestCase {
343343
}
344344

345345
// Configure Authenticator with result callback
346-
let config = Authenticator.Configuration(appCredentials: Self.mockCredentials,
347-
tokenHandling: tokenHandling,
348-
mode: .manualOnly,
349-
userAuthenticator: Authenticator.failingUserAuthenticator,
350-
authenticationStatusHandler: authenticationCallback)
346+
let config = Authenticator<Data>.Configuration(appCredentials: Self.mockCredentials,
347+
tokenHandling: tokenHandling,
348+
mode: .manualOnly,
349+
userAuthenticator: Authenticator<Data>.failingUserAuthenticator,
350+
authenticationStatusHandler: authenticationCallback)
351351

352352
let auth = Authenticator(config: config, urlLoader: nil)
353353
do {
@@ -395,10 +395,10 @@ final class AuthenticatorTests: XCTestCase {
395395
XCTAssertEqual(login.accessToken.value, "REFRESHED")
396396
}
397397

398-
let config = Authenticator.Configuration(appCredentials: Self.mockCredentials,
399-
loginStorage: storage,
400-
tokenHandling: tokenHandling,
401-
userAuthenticator: Self.disabledUserAuthenticator)
398+
let config = Authenticator<Data>.Configuration(appCredentials: Self.mockCredentials,
399+
loginStorage: storage,
400+
tokenHandling: tokenHandling,
401+
userAuthenticator: Self.disabledUserAuthenticator)
402402

403403
let auth = Authenticator(config: config, urlLoader: mockLoader.responseProvider)
404404

@@ -453,10 +453,10 @@ final class AuthenticatorTests: XCTestCase {
453453
savedLogins.append(login)
454454
}
455455

456-
let config = Authenticator.Configuration(appCredentials: Self.mockCredentials,
457-
loginStorage: storage,
458-
tokenHandling: tokenHandling,
459-
userAuthenticator: Self.disabledUserAuthenticator)
456+
let config = Authenticator<Data>.Configuration(appCredentials: Self.mockCredentials,
457+
loginStorage: storage,
458+
tokenHandling: tokenHandling,
459+
userAuthenticator: Self.disabledUserAuthenticator)
460460

461461
let auth = Authenticator(config: config, urlLoader: mockLoader)
462462

Tests/OAuthenticatorTests/GoogleTests.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,10 @@ final class GoogleTests: XCTestCase {
4444

4545
let creds = AppCredentials(clientId: "client_id", clientPassword: "client_pwd", scopes: ["scope1", "scope2"], callbackURL: callback!)
4646
let tokenHandling = GoogleAPI.googleAPITokenHandling(with: googleParameters)
47-
let config = Authenticator.Configuration(
47+
let config = Authenticator<Data>.Configuration(
4848
appCredentials: creds,
4949
tokenHandling: tokenHandling,
50-
userAuthenticator: Authenticator.failingUserAuthenticator
50+
userAuthenticator: Authenticator<Data>.failingUserAuthenticator
5151
)
5252

5353
// Validate URL is properly constructed
@@ -76,10 +76,10 @@ final class GoogleTests: XCTestCase {
7676

7777
let creds = AppCredentials(clientId: "client_id", clientPassword: "client_pwd", scopes: ["scope1", "scope2"], callbackURL: callback!)
7878
let tokenHandling = GoogleAPI.googleAPITokenHandling(with: googleParameters)
79-
let config = Authenticator.Configuration(
79+
let config = Authenticator<Data>.Configuration(
8080
appCredentials: creds,
8181
tokenHandling: tokenHandling,
82-
userAuthenticator: Authenticator.failingUserAuthenticator
82+
userAuthenticator: Authenticator<Data>.failingUserAuthenticator
8383
)
8484

8585
// Validate URL is properly constructed

0 commit comments

Comments
 (0)