~ knotless Search/
↑↓ move↵ open⇧↵ all results

johnandersen777.bsky.social/atirc-ios-client

0
1
main
60 lines · 2.3 KB Raw History
//
//  RequesterOAuthBridge.swift
//  AT-IRC
//
//  The embedded socialweb requester signs in with its own OAuth client (the
//  MarketIdentity). It redirects to a loopback URI; this bridge keeps one
//  loopback listener for the app's lifetime and forwards each callback to the
//  in-process requester (CompleteOAuth), like the page's
//  /oauth/callback/requester route.
//

import Foundation
import OSLog

@MainActor
final class RequesterOAuthBridge {
    private var server: LoopbackServer?
    private var task: Task<Void, Never>?
    private let kcp = KCPClient()
    private let log = Logger(subsystem: "com.fedfork.irc.AT-IRC", category: "market-oauth")

    /// Start the redirect listener and return the redirect URI for the
    /// requester config. Idempotent.
    func start() async throws -> String {
        if let server, server.port != 0 {
            return "http://127.0.0.1:\(server.port)/"
        }
        let server = LoopbackServer()
        let port = try await server.start()
        self.server = server
        task?.cancel()
        task = Task { @MainActor [weak self] in
            while !Task.isCancelled {
                guard let self, let server = self.server else { return }
                guard let query = try? await server.waitForCallback() else { return }
                server.resetCallback()
                log.notice("callback received keys=\(query.keys.sorted().joined(separator: ","), privacy: .public)")
                var components = URLComponents()
                components.queryItems = query.map { URLQueryItem(name: $0.key, value: $0.value) }
                let path = "/oauth/callback/requester?" + (components.percentEncodedQuery ?? "")
                do {
                    // This route belongs to the requester, not the
                    // apiserver: do not send X-Atirc-Direct.
                    let response = try await self.kcp.request("GET", path, direct: false)
                    log.notice("callback forwarded status=\(response.status, privacy: .public)")
                } catch {
                    log.error("callback forward failed: \(error.localizedDescription, privacy: .public)")
                }
            }
        }
        return "http://127.0.0.1:\(port)/"
    }

    func stop() {
        task?.cancel()
        task = nil
        server?.stop()
        server = nil
    }
}