// // FeedlyAccountDelegate+OAuth.swift // Account // // Created by Kiel Gillard on 14/9/19. // Copyright © 2019 Ranchero Software, LLC. All rights reserved. // import Foundation import RSWeb /// Models the access token response from Feedly. /// https://developer.feedly.com/v3/auth/#exchanging-an-auth-code-for-a-refresh-token-and-an-access-token public struct FeedlyOAuthAccessTokenResponse: Decodable, OAuthAccessTokenResponse { /// The ID of the Feedly user. public var id: String // Required properties of the OAuth 2.0 Authorization Framework section 4.1.4. public var accessToken: String public var tokenType: String public var expiresIn: Int public var refreshToken: String? public var scope: String } extension FeedlyAccountDelegate: OAuthAuthorizationGranting { private static let oauthAuthorizationGrantScope = "https://cloud.feedly.com/subscriptions" static func oauthAuthorizationCodeGrantRequest() -> URLRequest { let client = environment.oauthAuthorizationClient 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: Transport, completionHandler: @escaping (Result) -> ()) { let client = environment.oauthAuthorizationClient let request = OAuthAccessTokenRequest(authorizationResponse: response, scope: oauthAuthorizationGrantScope, client: client) let caller = FeedlyAPICaller(transport: transport, api: environment) caller.requestAccessToken(request) { result in switch result { case .success(let response): 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) completionHandler(.success(grant)) case .failure(let error): completionHandler(.failure(error)) } } } } extension FeedlyAccountDelegate: OAuthAccessTokenRefreshing { func refreshAccessToken(with refreshToken: String, client: OAuthAuthorizationClient, completionHandler: @escaping (Result) -> ()) { let request = OAuthRefreshAccessTokenRequest(refreshToken: refreshToken, scope: nil, client: client) caller.refreshAccessToken(request) { result in switch result { case .success(let response): 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) completionHandler(.success(grant)) case .failure(let error): completionHandler(.failure(error)) } } } }