@@ -78,6 +78,252 @@ Class | Method | HTTP request | Description
7878
7979{ {/authMethods} }
8080
81+ { {#useURLSession} }
82+ ### How do I implement bearer token authentication with URLSession on the Swift 5 API client?
83+
84+ First you subclass RequestBuilderFactory
85+ ```
86+ class BearerRequestBuilderFactory: RequestBuilderFactory {
87+ func getNonDecodableBuilder< T> () -> RequestBuilder< T> .Type {
88+ BearerRequestBuilder< T> .self
89+ }
90+
91+ func getBuilder<T: Decodable >() -> RequestBuilder<T >.Type {
92+ BearerDecodableRequestBuilder< T> .self
93+ }
94+ }
95+ ```
96+
97+ Then you subclass URLSessionRequestBuilder and URLSessionDecodableRequestBuilder
98+ ```
99+ class BearerRequestBuilder<T >: URLSessionRequestBuilder<T > {
100+ @discardableResult
101+ override func execute(_ apiResponseQueue: DispatchQueue = PetstoreClientAPI.apiResponseQueue, _ completion: @escaping (Result< Response< T> , ErrorResponse> ) -> Void) -> RequestTask {
102+
103+ // Before making the request, we can validate if we have a bearer token to be able to make a request
104+ BearerTokenHandler.refreshTokenIfDoesntExist {
105+
106+ // Here we make the request
107+ super.execute(apiResponseQueue) { result in
108+
109+ switch result {
110+ case .success:
111+ // If we got a successful response, we send the response to the completion block
112+ completion(result)
113+
114+ case let .failure(error):
115+
116+ // If we got a failure response, we will analyse the error to see what we should do with it
117+ if case let ErrorResponse.error(_, data, response, error) = error {
118+
119+ // If the error is an ErrorResponse.error() we will analyse it to see if it' s a 401, and if it' s a 401, we will refresh the token and retry the request
120+ BearerTokenHandler.refreshTokenIfUnauthorizedRequestResponse(
121+ data: data,
122+ response: response,
123+ error: error
124+ ) { wasTokenRefreshed in
125+
126+ if wasTokenRefreshed {
127+ // If the token was refreshed, it' s because it was a 401 error, so we refreshed the token, and we are going to retry the request by calling self.execute()
128+ self.execute(apiResponseQueue, completion)
129+ } else {
130+ // If the token was not refreshed, it' s because it was not a 401 error, so we send the response to the completion block
131+ completion(result)
132+ }
133+ }
134+ } else {
135+ // If it' s an unknown error, we send the response to the completion block
136+ completion(result)
137+ }
138+
139+ }
140+ }
141+ }
142+
143+ return requestTask
144+ }
145+ }
146+
147+ class BearerDecodableRequestBuilder<T: Decodable>: URLSessionDecodableRequestBuilder<T> {
148+ @discardableResult
149+ override func execute(_ apiResponseQueue: DispatchQueue = PetstoreClientAPI.apiResponseQueue, _ completion: @escaping (Result<Response<T>, ErrorResponse>) -> Void) -> RequestTask {
150+ // Before making the request, we can validate if we have a bearer token to be able to make a request
151+ BearerTokenHandler.refreshTokenIfDoesntExist {
152+
153+ // Here we make the request
154+ super.execute(apiResponseQueue) { result in
155+
156+ switch result {
157+ case .success:
158+ // If we got a successful response, we send the response to the completion block
159+ completion(result)
160+
161+ case let .failure(error):
162+
163+ // If we got a failure response, we will analyse the error to see what we should do with it
164+ if case let ErrorResponse.error(_, data, response, error) = error {
165+
166+ // If the error is an ErrorResponse.error() we will analyse it to see if it' s a 401, and if it' s a 401, we will refresh the token and retry the request
167+ BearerTokenHandler.refreshTokenIfUnauthorizedRequestResponse(
168+ data: data,
169+ response: response,
170+ error: error
171+ ) { wasTokenRefreshed in
172+
173+ if wasTokenRefreshed {
174+ // If the token was refreshed, it' s because it was a 401 error, so we refreshed the token, and we are going to retry the request by calling self.execute()
175+ self.execute(apiResponseQueue, completion)
176+ } else {
177+ // If the token was not refreshed, it' s because it was not a 401 error, so we send the response to the completion block
178+ completion(result)
179+ }
180+ }
181+ } else {
182+ // If it' s an unknown error, we send the response to the completion block
183+ completion(result)
184+ }
185+
186+ }
187+ }
188+ }
189+
190+ return requestTask
191+ }
192+ }
193+
194+ class BearerTokenHandler {
195+ private static var bearerToken: String? = nil
196+
197+ static func refreshTokenIfDoesntExist(completionHandler: @escaping () -> Void) {
198+ if bearerToken != nil {
199+ completionHandler()
200+ } else {
201+ startRefreshingToken {
202+ completionHandler()
203+ }
204+ }
205+ }
206+
207+ static func refreshTokenIfUnauthorizedRequestResponse(data: Data?, response: URLResponse?, error: Error?, completionHandler: @escaping (Bool) -> Void) {
208+ if let response = response as? HTTPURLResponse, response.statusCode == 401 {
209+ startRefreshingToken {
210+ completionHandler(true )
211+ }
212+ } else {
213+ completionHandler(false )
214+ }
215+ }
216+
217+ private static func startRefreshingToken(completionHandler: @escaping () -> Void) {
218+ // Get a bearer token
219+ let dummyBearerToken = " ..."
220+
221+ bearerToken = dummyBearerToken
222+ PetstoreClientAPI.customHeaders[" Authorization" ] = " Bearer \( dummyBearerToken)"
223+
224+ completionHandler()
225+ }
226+ }
227+ ```
228+
229+ Then you assign the `BearerRequestBuilderFactory` to the property `requestBuilderFactory`.
230+
231+ `PetstoreClientAPI.requestBuilderFactory = BearerRequestBuilderFactory()`
232+
233+ The name `PetstoreClientAPI.requestBuilderFactory` will change depending on your project name.
234+
235+ Here is a working sample that put's together all of this.
236+ [AppDelegate.swift](https://github.com/OpenAPITools/openapi-generator/blob/master/samples/client/petstore/swift5/urlsessionLibrary/SwaggerClientTests/SwaggerClient/AppDelegate.swift)
237+ [BearerDecodableRequestBuilder.swift](https://github.com/OpenAPITools/openapi-generator/blob/master/samples/client/petstore/swift5/urlsessionLibrary/SwaggerClientTests/SwaggerClient/BearerDecodableRequestBuilder.swift)
238+ { {/useURLSession} }
239+ { {#useAlamofire} }
240+ ### How do I implement bearer token authentication with Alamofire on the Swift 5 API client?
241+
242+ First you subclass RequestBuilderFactory
243+ ```
244+ class BearerRequestBuilderFactory: RequestBuilderFactory {
245+ func getNonDecodableBuilder< T> () -> RequestBuilder< T> .Type {
246+ BearerRequestBuilder< T> .self
247+ }
248+
249+ func getBuilder<T: Decodable >() -> RequestBuilder<T >.Type {
250+ BearerDecodableRequestBuilder< T> .self
251+ }
252+ }
253+ ```
254+
255+ Then you subclass AlamofireRequestBuilder and AlamofireDecodableRequestBuilder
256+ ```
257+ class BearerRequestBuilder<T >: AlamofireRequestBuilder<T > {
258+ override func createSessionManager() -> SessionManager {
259+ let sessionManager = super.createSessionManager()
260+
261+ let bearerTokenHandler = BearerTokenHandler()
262+ sessionManager.adapter = bearerTokenHandler
263+ sessionManager.retrier = bearerTokenHandler
264+
265+ return sessionManager
266+ }
267+ }
268+
269+ class BearerDecodableRequestBuilder<T: Decodable >: AlamofireDecodableRequestBuilder<T > {
270+ override func createSessionManager() -> SessionManager {
271+ let sessionManager = super.createSessionManager()
272+
273+ let bearerTokenHandler = BearerTokenHandler()
274+ sessionManager.adapter = bearerTokenHandler
275+ sessionManager.retrier = bearerTokenHandler
276+
277+ return sessionManager
278+ }
279+ }
280+
281+ class BearerTokenHandler: RequestAdapter, RequestRetrier {
282+ private static var bearerToken: String? = nil
283+
284+ func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
285+ if let bearerToken = Self.bearerToken {
286+ var urlRequest = urlRequest
287+ urlRequest.setValue(" Bearer \( bearerToken)" , forHTTPHeaderField: " Authorization" )
288+ return urlRequest
289+ }
290+
291+ return urlRequest
292+ }
293+
294+ func should(_: SessionManager, retry request: Request, with _: Error, completion: @escaping RequestRetryCompletion) {
295+ if let response = request.task?.response as? HTTPURLResponse, response.statusCode == 401 {
296+ Self.startRefreshingToken { isTokenRefreshed in
297+ completion(isTokenRefreshed, 0.0)
298+ }
299+ } else {
300+ completion(false , 0.0)
301+ }
302+ }
303+
304+ private static func startRefreshingToken(completionHandler: @escaping (Bool) -> Void) {
305+ // Get a bearer token
306+ let dummyBearerToken = " ..."
307+
308+ bearerToken = dummyBearerToken
309+ PetstoreClientAPI.customHeaders[" Authorization" ] = " Bearer \( dummyBearerToken)"
310+
311+ completionHandler(true )
312+ }
313+ }
314+ ```
315+
316+ Then you assign the `BearerRequestBuilderFactory` to the property `requestBuilderFactory`.
317+
318+ `PetstoreClientAPI.requestBuilderFactory = BearerRequestBuilderFactory()`
319+
320+ The name `PetstoreClientAPI.requestBuilderFactory` will change depending on your project name.
321+
322+ Here is a working sample that put's together all of this.
323+ [AppDelegate.swift](https://github.com/OpenAPITools/openapi-generator/blob/master/samples/client/petstore/swift5/alamofireLibrary/SwaggerClientTests/SwaggerClient/AppDelegate.swift)
324+ [BearerTokenHandler.swift](https://github.com/OpenAPITools/openapi-generator/blob/master/samples/client/petstore/swift5/alamofireLibrary/SwaggerClientTests/SwaggerClient/BearerDecodableRequestBuilder.swift)
325+ { {/useAlamofire} }
326+
81327## Author
82328
83329{ {#apiInfo} }{ {#apis} }{ {#-last} }{ {infoEmail} }
0 commit comments