// // GetAuthorizationTokenService.swift // Tusker // // Created by Shadowfacts on 4/9/24. // Copyright © 2024 Shadowfacts. All rights reserved. // import Foundation import AuthenticationServices class GetAuthorizationTokenService { let instanceURL: URL let clientID: String let presentationContextProvider: ASWebAuthenticationPresentationContextProviding init(instanceURL: URL, clientID: String, presentationContextProvider: ASWebAuthenticationPresentationContextProviding) { self.instanceURL = instanceURL self.clientID = clientID self.presentationContextProvider = presentationContextProvider } @MainActor func run() async throws -> String { var components = URLComponents(url: instanceURL, resolvingAgainstBaseURL: false)! components.path = "/oauth/authorize" components.queryItems = [ URLQueryItem(name: "client_id", value: clientID), URLQueryItem(name: "response_type", value: "code"), URLQueryItem(name: "scope", value: MastodonController.oauthScopes.map(\.rawValue).joined(separator: " ")), URLQueryItem(name: "redirect_uri", value: "tusker://oauth") ] let authorizeURL = components.url! return try await withCheckedThrowingContinuation({ continuation in let authenticationSession = ASWebAuthenticationSession(url: authorizeURL, callbackURLScheme: "tusker", completionHandler: { url, error in if let error = error { if (error as? ASWebAuthenticationSessionError)?.code == .canceledLogin { continuation.resume(throwing: Error.cancelled) } else { continuation.resume(throwing: error) } } else if let url = url, let components = URLComponents(url: url, resolvingAgainstBaseURL: true), let item = components.queryItems?.first(where: { $0.name == "code" }), let code = item.value { continuation.resume(returning: code) } else { continuation.resume(throwing: Error.noAuthorizationCode) } }) // Prefer ephemeral sessions to make it easier to sign into multiple accounts on the same instance. authenticationSession.prefersEphemeralWebBrowserSession = true authenticationSession.presentationContextProvider = presentationContextProvider authenticationSession.start() }) } enum Error: Swift.Error { case cancelled case noAuthorizationCode } }