rastrillo / native Public

Clone
git clone https://amadan.net/rastrillo/native

Plain git — no account needed to clone.

Download

Download this file

1import AuthenticationServices
2import Foundation
3#if os(iOS)
4import UIKit
5#elseif os(macOS)
6import AppKit
7#endif
8
9/// Keeps browser authentication attached to the app, including servers that still
10/// finish through the proof-bound exchange instead of issuing a callback.
11@MainActor public final class NativeBrowserSignIn: NSObject, ASWebAuthenticationPresentationContextProviding {
12 private var session: ASWebAuthenticationSession?
13 private var generation = UUID()
14 public override init() { super.init() }
15
16 public func start(url: URL, scheme: String, completion: @escaping @MainActor (URL?) -> Void) -> Bool {
17 cancel()
18 let generation = self.generation
19 let session = ASWebAuthenticationSession(url: url, callbackURLScheme: scheme) { url, _ in
20 Task { @MainActor [weak self] in
21 guard self?.generation == generation else { return }
22 completion(url)
23 }
24 }
25 session.presentationContextProvider = self
26 session.prefersEphemeralWebBrowserSession = false
27 self.session = session
28 return session.start()
29 }
30
31 public func cancel() {
32 generation = UUID()
33 session?.cancel()
34 session = nil
35 }
36
37 public func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
38 #if os(iOS)
39 return UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }
40 .filter { $0.activationState == .foregroundActive }
41 .flatMap(\.windows).first(where: \.isKeyWindow) ?? ASPresentationAnchor()
42 #else
43 return NSApplication.shared.keyWindow ?? NSApplication.shared.windows.first ?? ASPresentationAnchor()
44 #endif
45 }
46}
47