import AuthenticationServices import Foundation #if os(iOS) import UIKit #elseif os(macOS) import AppKit #endif /// Keeps browser authentication attached to the app, including servers that still /// finish through the proof-bound exchange instead of issuing a callback. @MainActor public final class NativeBrowserSignIn: NSObject, ASWebAuthenticationPresentationContextProviding { private var session: ASWebAuthenticationSession? private var generation = UUID() public override init() { super.init() } public func start(url: URL, scheme: String, completion: @escaping @MainActor (URL?) -> Void) -> Bool { cancel() let generation = self.generation let session = ASWebAuthenticationSession(url: url, callbackURLScheme: scheme) { url, _ in Task { @MainActor [weak self] in guard self?.generation == generation else { return } completion(url) } } session.presentationContextProvider = self session.prefersEphemeralWebBrowserSession = false self.session = session return session.start() } public func cancel() { generation = UUID() session?.cancel() session = nil } public func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor { #if os(iOS) return UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene } .filter { $0.activationState == .foregroundActive } .flatMap(\.windows).first(where: \.isKeyWindow) ?? ASPresentationAnchor() #else return NSApplication.shared.keyWindow ?? NSApplication.shared.windows.first ?? ASPresentationAnchor() #endif } }