From 9952bb98ba914e0c180756c65a05511bbabbd335 Mon Sep 17 00:00:00 2001 From: Volodymyr Chekyrta <127732735+volodymyr-chekyrta@users.noreply.github.com> Date: Mon, 23 Oct 2023 16:28:20 +0300 Subject: [PATCH] Added support of JWT tokens. (#104) The type of access token can be changed in the Core.Config class. --- Core/Core/Configuration/Config.swift | 8 ++ .../Core/Data/Repository/AuthRepository.swift | 3 +- Core/Core/Network/AuthEndpoint.swift | 8 +- Core/Core/Network/RequestInterceptor.swift | 85 ++++++++++--------- 4 files changed, 59 insertions(+), 45 deletions(-) diff --git a/Core/Core/Configuration/Config.swift b/Core/Core/Configuration/Config.swift index 7f49fac57..77f5da816 100644 --- a/Core/Core/Configuration/Config.swift +++ b/Core/Core/Configuration/Config.swift @@ -11,6 +11,7 @@ public class Config { public let baseURL: URL public let oAuthClientId: String + public let tokenType: TokenType = .jwt public lazy var termsOfUse: URL? = { URL(string: "\(baseURL.description)/tos") @@ -31,6 +32,13 @@ public class Config { } } +public extension Config { + enum TokenType: String { + case jwt = "JWT" + case bearer = "BEARER" + } +} + // Mark - For testing and SwiftUI preview #if DEBUG public class ConfigMock: Config { diff --git a/Core/Core/Data/Repository/AuthRepository.swift b/Core/Core/Data/Repository/AuthRepository.swift index 1ed62fd68..874159e4c 100644 --- a/Core/Core/Data/Repository/AuthRepository.swift +++ b/Core/Core/Data/Repository/AuthRepository.swift @@ -33,7 +33,8 @@ public class AuthRepository: AuthRepositoryProtocol { let endPoint = AuthEndpoint.getAccessToken( username: username, password: password, - clientId: config.oAuthClientId + clientId: config.oAuthClientId, + tokenType: config.tokenType.rawValue ) let authResponse = try await api.requestData(endPoint).mapResponse(DataLayer.AuthResponse.self) guard let accessToken = authResponse.accessToken, diff --git a/Core/Core/Network/AuthEndpoint.swift b/Core/Core/Network/AuthEndpoint.swift index c477c451c..d92d139c0 100644 --- a/Core/Core/Network/AuthEndpoint.swift +++ b/Core/Core/Network/AuthEndpoint.swift @@ -9,7 +9,7 @@ import Foundation import Alamofire enum AuthEndpoint: EndPointType { - case getAccessToken(username: String, password: String, clientId: String) + case getAccessToken(username: String, password: String, clientId: String, tokenType: String) case getUserInfo case getAuthCookies case getRegisterFields @@ -61,12 +61,14 @@ enum AuthEndpoint: EndPointType { var task: HTTPTask { switch self { - case let .getAccessToken(username, password, clientId): + case let .getAccessToken(username, password, clientId, tokenType): let params: [String: Encodable] = [ "grant_type": Constants.GrantTypePassword, "client_id": clientId, "username": username, - "password": password + "password": password, + "token_type": tokenType, + "asymmetric_jwt": true ] return .requestParameters(parameters: params, encoding: URLEncoding.httpBody) case .getUserInfo: diff --git a/Core/Core/Network/RequestInterceptor.swift b/Core/Core/Network/RequestInterceptor.swift index cecdf0570..49f13a806 100644 --- a/Core/Core/Network/RequestInterceptor.swift +++ b/Core/Core/Network/RequestInterceptor.swift @@ -35,7 +35,7 @@ final public class RequestInterceptor: Alamofire.RequestInterceptor { // Set the Authorization header value using the access token. if let token = storage.accessToken { - urlRequest.setValue("Bearer " + token, forHTTPHeaderField: "Authorization") + urlRequest.setValue("\(config.tokenType.rawValue) \(token)", forHTTPHeaderField: "Authorization") } completion(.success(urlRequest)) @@ -84,49 +84,52 @@ final public class RequestInterceptor: Alamofire.RequestInterceptor { private func refreshToken( refreshToken: String, - completion: @escaping (_ succeeded: Bool) -> Void) { - guard !isRefreshing else { return } - - isRefreshing = true - - let url = config.baseURL.appendingPathComponent("/oauth2/access_token") - - let parameters = [ - "grant_type": Constants.GrantTypeRefreshToken, - "client_id": config.oAuthClientId, - "refresh_token": refreshToken - ] - AF.request( - url, - method: .post, - parameters: parameters, - encoding: URLEncoding.httpBody - ).response { [weak self] response in - guard let self = self else { return } - switch response.result { - case let .success(data): - do { - let json = try JSONSerialization.jsonObject( - with: data!, - options: .mutableContainers - ) as? [String: AnyObject] - guard let json, - let accessToken = json["access_token"] as? String, - let refreshToken = json["refresh_token"] as? String, - accessToken.count > 0, - refreshToken.count > 0 else { - return completion(false) - } - self.storage.accessToken = accessToken - self.storage.refreshToken = refreshToken - completion(true) - } catch { - completion(false) + completion: @escaping (_ succeeded: Bool) -> Void + ) { + guard !isRefreshing else { return } + + isRefreshing = true + + let url = config.baseURL.appendingPathComponent("/oauth2/access_token") + + let parameters: [String: Encodable] = [ + "grant_type": Constants.GrantTypeRefreshToken, + "client_id": config.oAuthClientId, + "refresh_token": refreshToken, + "token_type": config.tokenType.rawValue, + "asymmetric_jwt": true + ] + AF.request( + url, + method: .post, + parameters: parameters, + encoding: URLEncoding.httpBody + ).response { [weak self] response in + guard let self = self else { return } + switch response.result { + case let .success(data): + do { + let json = try JSONSerialization.jsonObject( + with: data!, + options: .mutableContainers + ) as? [String: AnyObject] + guard let json, + let accessToken = json["access_token"] as? String, + let refreshToken = json["refresh_token"] as? String, + accessToken.count > 0, + refreshToken.count > 0 else { + return completion(false) } - case .failure: + self.storage.accessToken = accessToken + self.storage.refreshToken = refreshToken + completion(true) + } catch { completion(false) } - self.isRefreshing = false + case .failure: + completion(false) } + self.isRefreshing = false } + } }