Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
89 lines
2.4 KiB
Swift
89 lines
2.4 KiB
Swift
import AuthenticationServices
|
|
import Foundation
|
|
import UIKit
|
|
|
|
enum MobileWebAuthenticationResult {
|
|
case callback
|
|
case cancelled
|
|
case failed
|
|
}
|
|
|
|
final class MobileWebAuthenticationSession: NSObject,
|
|
ASWebAuthenticationPresentationContextProviding
|
|
{
|
|
private var authenticationSession: ASWebAuthenticationSession?
|
|
private weak var presentationAnchor: ASPresentationAnchor?
|
|
|
|
func start(
|
|
url: URL,
|
|
callbackScheme: String,
|
|
completion: @escaping (MobileWebAuthenticationResult) -> Void
|
|
) -> Bool {
|
|
guard authenticationSession == nil, let anchor = Self.currentAnchor() else {
|
|
return false
|
|
}
|
|
|
|
let completionHandler: ASWebAuthenticationSession.CompletionHandler = {
|
|
[weak self] _, error in
|
|
self?.authenticationSession = nil
|
|
self?.presentationAnchor = nil
|
|
guard let error else {
|
|
completion(.callback)
|
|
return
|
|
}
|
|
let browserError = error as NSError
|
|
let cancelled =
|
|
browserError.domain == ASWebAuthenticationSessionError.errorDomain
|
|
&& browserError.code
|
|
== ASWebAuthenticationSessionError.Code.canceledLogin.rawValue
|
|
completion(cancelled ? .cancelled : .failed)
|
|
}
|
|
let session: ASWebAuthenticationSession
|
|
if #available(iOS 17.4, *) {
|
|
session = ASWebAuthenticationSession(
|
|
url: url,
|
|
callback: .customScheme(callbackScheme),
|
|
completionHandler: completionHandler
|
|
)
|
|
} else {
|
|
session = ASWebAuthenticationSession(
|
|
url: url,
|
|
callbackURLScheme: callbackScheme,
|
|
completionHandler: completionHandler
|
|
)
|
|
}
|
|
presentationAnchor = anchor
|
|
authenticationSession = session
|
|
session.presentationContextProvider = self
|
|
session.prefersEphemeralWebBrowserSession = false
|
|
guard session.start() else {
|
|
authenticationSession = nil
|
|
presentationAnchor = nil
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func cancel() {
|
|
authenticationSession?.cancel()
|
|
authenticationSession = nil
|
|
presentationAnchor = nil
|
|
}
|
|
|
|
func presentationAnchor(
|
|
for _: ASWebAuthenticationSession
|
|
) -> ASPresentationAnchor {
|
|
presentationAnchor ?? ASPresentationAnchor()
|
|
}
|
|
|
|
private static func currentAnchor() -> ASPresentationAnchor? {
|
|
let scenes = UIApplication.shared.connectedScenes.compactMap {
|
|
$0 as? UIWindowScene
|
|
}
|
|
return scenes
|
|
.flatMap(\.windows)
|
|
.first(where: \.isKeyWindow)
|
|
?? scenes.flatMap(\.windows).first
|
|
}
|
|
}
|