diff --git a/Account/Sources/Account/Account.swift b/Account/Sources/Account/Account.swift index 25762943c..fce8c1b42 100644 --- a/Account/Sources/Account/Account.swift +++ b/Account/Sources/Account/Account.swift @@ -377,27 +377,6 @@ public enum FetchType { } } - internal static func oauthAuthorizationClient(for type: AccountType, secretsProvider: SecretsProvider) -> OAuthAuthorizationClient { - switch type { - case .feedly: - return FeedlyAccountDelegate.environment.oauthAuthorizationClient(secretsProvider: secretsProvider) - default: - fatalError("\(type) is not a client for OAuth authorization code granting.") - } - } - - public static func requestOAuthAccessToken(with response: OAuthAuthorizationResponse, - client: OAuthAuthorizationClient, - accountType: AccountType, - transport: Transport = URLSession.webserviceTransport(), - secretsProvider: SecretsProvider) async throws -> OAuthAuthorizationGrant { - - guard accountType == .feedly else { - fatalError("\(accountType) does not support OAuth authorization code granting.") - } - return try await FeedlyAccountDelegate.requestOAuthAccessToken(with: response, transport: transport, secretsProvider: secretsProvider) - } - public func receiveRemoteNotification(userInfo: [AnyHashable: Any]) async { await delegate.receiveRemoteNotification(for: self, userInfo: userInfo) } diff --git a/Account/Sources/Account/AccountDelegates/FeedlyAccountDelegate+OAuth.swift b/Account/Sources/Account/AccountDelegates/FeedlyAccountDelegate+OAuth.swift index c49503511..e3a7acf95 100644 --- a/Account/Sources/Account/AccountDelegates/FeedlyAccountDelegate+OAuth.swift +++ b/Account/Sources/Account/AccountDelegates/FeedlyAccountDelegate+OAuth.swift @@ -11,46 +11,8 @@ import Web import Secrets import Feedly - extension FeedlyAccountDelegate { - private static let oauthAuthorizationGrantScope = "https://cloud.feedly.com/subscriptions" - - static func oauthAuthorizationCodeGrantRequest(secretsProvider: SecretsProvider) -> URLRequest { - let client = environment.oauthAuthorizationClient(secretsProvider: secretsProvider) - let authorizationRequest = OAuthAuthorizationRequest(clientID: client.id, - redirectURI: client.redirectURI, - scope: oauthAuthorizationGrantScope, - state: client.state) - let baseURLComponents = environment.baseUrlComponents - return FeedlyAPICaller.authorizationCodeURLRequest(for: authorizationRequest, baseUrlComponents: baseURLComponents) - } - - static func requestOAuthAccessToken(with response: OAuthAuthorizationResponse, transport: any Web.Transport, secretsProvider: any Secrets.SecretsProvider) async throws -> OAuthAuthorizationGrant { - - let client = environment.oauthAuthorizationClient(secretsProvider: secretsProvider) - let request = OAuthAccessTokenRequest(authorizationResponse: response, - scope: oauthAuthorizationGrantScope, - client: client) - let caller = FeedlyAPICaller(transport: transport, api: environment, secretsProvider: secretsProvider) - let response = try await caller.requestAccessToken(request) - - let accessToken = Credentials(type: .oauthAccessToken, username: response.id, secret: response.accessToken) - let refreshToken: Credentials? = { - guard let token = response.refreshToken else { - return nil - } - return Credentials(type: .oauthRefreshToken, username: response.id, secret: token) - }() - - let grant = OAuthAuthorizationGrant(accessToken: accessToken, refreshToken: refreshToken) - - return grant - } -} - -extension FeedlyAccountDelegate { - func refreshAccessToken(with refreshToken: String, client: OAuthAuthorizationClient) async throws -> OAuthAuthorizationGrant { let request = OAuthRefreshAccessTokenRequest(refreshToken: refreshToken, scope: nil, client: client) diff --git a/Account/Sources/Account/Feedly/OAuthAccountAuthorizationOperation.swift b/Account/Sources/Account/Feedly/OAuthAccountAuthorizationOperation.swift index a0dea9e0e..bd10c7622 100644 --- a/Account/Sources/Account/Feedly/OAuthAccountAuthorizationOperation.swift +++ b/Account/Sources/Account/Feedly/OAuthAccountAuthorizationOperation.swift @@ -50,13 +50,13 @@ public enum OAuthAccountAuthorizationOperationError: LocalizedError { public init(accountType: AccountType, secretsProvider: SecretsProvider) { self.accountType = accountType self.secretsProvider = secretsProvider - self.oauthClient = Account.oauthAuthorizationClient(for: accountType, secretsProvider: secretsProvider) + self.oauthClient = FeedlyAPICaller.API.cloud.oauthAuthorizationClient(secretsProvider: secretsProvider) } @MainActor public func run() { assert(presentationAnchor != nil, "\(self) outlived presentation anchor.") - let request = FeedlyAccountDelegate.oauthAuthorizationCodeGrantRequest(secretsProvider: secretsProvider) + let request = FeedlyAPICaller.oauthAuthorizationCodeGrantRequest(secretsProvider: secretsProvider) guard let url = request.url else { return DispatchQueue.main.async { @@ -120,7 +120,7 @@ public enum OAuthAccountAuthorizationOperationError: LocalizedError { let response = try OAuthAuthorizationResponse(url: url, client: self.oauthClient) - let tokenResponse = try await Account.requestOAuthAccessToken(with: response, client: oauthClient, accountType: accountType, secretsProvider: secretsProvider) + let tokenResponse = try await FeedlyAPICaller.requestOAuthAccessToken(with: response, transport: URLSession.webserviceTransport(), secretsProvider: secretsProvider) saveAccount(for: tokenResponse) } catch is ASWebAuthenticationSessionError { diff --git a/Feedly/Sources/Feedly/FeedlyAPICaller.swift b/Feedly/Sources/Feedly/FeedlyAPICaller.swift index 2049f8382..ddec70c68 100644 --- a/Feedly/Sources/Feedly/FeedlyAPICaller.swift +++ b/Feedly/Sources/Feedly/FeedlyAPICaller.swift @@ -51,11 +51,13 @@ public protocol FeedlyAPICallerDelegate: AnyObject { private let baseURLComponents: URLComponents private let uriComponentAllowed: CharacterSet private let secretsProvider: SecretsProvider - + private let api: FeedlyAPICaller.API + public init(transport: Transport, api: API, secretsProvider: SecretsProvider) { self.transport = transport self.baseURLComponents = api.baseUrlComponents self.secretsProvider = secretsProvider + self.api = api var urlHostAllowed = CharacterSet.urlHostAllowed urlHostAllowed.remove("+") @@ -578,6 +580,45 @@ extension FeedlyAPICaller { } } +// MARK: - OAuth + +extension FeedlyAPICaller { + + private static let oauthAuthorizationGrantScope = "https://cloud.feedly.com/subscriptions" + + public static func oauthAuthorizationCodeGrantRequest(secretsProvider: SecretsProvider) -> URLRequest { + let client = API.cloud.oauthAuthorizationClient(secretsProvider: secretsProvider) + let authorizationRequest = OAuthAuthorizationRequest(clientID: client.id, + redirectURI: client.redirectURI, + scope: oauthAuthorizationGrantScope, + state: client.state) + let baseURLComponents = API.cloud.baseUrlComponents + return FeedlyAPICaller.authorizationCodeURLRequest(for: authorizationRequest, baseUrlComponents: baseURLComponents) + } + + public static func requestOAuthAccessToken(with response: OAuthAuthorizationResponse, transport: any Web.Transport, secretsProvider: any Secrets.SecretsProvider) async throws -> OAuthAuthorizationGrant { + + let client = API.cloud.oauthAuthorizationClient(secretsProvider: secretsProvider) + let request = OAuthAccessTokenRequest(authorizationResponse: response, + scope: oauthAuthorizationGrantScope, + client: client) + let caller = FeedlyAPICaller(transport: transport, api: .cloud, secretsProvider: secretsProvider) + let response = try await caller.requestAccessToken(request) + + let accessToken = Credentials(type: .oauthAccessToken, username: response.id, secret: response.accessToken) + let refreshToken: Credentials? = { + guard let token = response.refreshToken else { + return nil + } + return Credentials(type: .oauthRefreshToken, username: response.id, secret: token) + }() + + let grant = OAuthAuthorizationGrant(accessToken: accessToken, refreshToken: refreshToken) + + return grant + } +} + private extension FeedlyAPICaller { func urlRequest(path: String, method: String, includeJSONHeaders: Bool, includeOAuthToken: Bool) throws -> URLRequest {