p2 / OAuth2

OAuth2 framework for macOS and iOS, written in Swift.
Other
1.14k stars 275 forks source link

mac catalyst can not open login window #401

Open rushairer opened 1 year ago

rushairer commented 1 year ago

OAuth 5.3.2/ SwiftUI/ using macOS 13 run catalyst mode, will get this error:

2022-11-17 15:42:03.532115+0800 Crossword[1778:4730281] [TraitCollection] Class _UIFindNavigatorViewController overrides the -traitCollection getter, which is not supported. If you're trying to override traits, you must use the appropriate API.
2022-11-17 15:42:20.836892+0800 Crossword[1778:4730281] [AXRuntimeCommon] Unknown client: Crossword
[Debug] OAuth2: Starting authorization
[Debug] OAuth2: No access token, checking if a refresh token is available
[Debug] OAuth2: Error refreshing token: I don't have a refresh token, not trying to refresh
[Debug] OAuth2: Opening authorize URL embedded:  *****************HIDDEN*****************
[Debug] OAuth2: The operation couldn’t be completed. (com.apple.AuthenticationServices.WebAuthenticationSession error 2.)
Authorization was canceled or went wrong: The operation couldn’t be completed. (com.apple.AuthenticationServices.WebAuthenticationSession error 2.).

Both iOS/Mac are OK, only catalyst can not work :(


import OAuth2
#if os(iOS)
import UIKit
#elseif os(macOS)
import AppKit
#endif
import Combine
import WebKit

public struct OAuth2Configuration {
    public init(clientId: String, authorizeUri: String, tokenUri: String, redirectUris: [String], scope: String, customParameters: OAuth2StringDict) {
        self.clientId = clientId
        self.authorizeUri = authorizeUri
        self.tokenUri = tokenUri
        self.redirectUris = redirectUris
        self.scope = scope
        self.customParameters = customParameters
    }

    let clientId: String
    let authorizeUri: String
    let tokenUri: String
    let redirectUris: [String]
    let scope: String
    let customParameters: OAuth2StringDict
    let logLevel: OAuth2LogLevel = .debug
}

public class OAuth2Coordinator: ObservableObject {
    // MARK: Stored Properties

    @Published var accessToken: String?

    public private(set) var text = "Hello, World!"

    private var oauth2: OAuth2?
    private var cancellables = Set<AnyCancellable>()

    // MARK: Initialization
    public init(configuration: OAuth2Configuration) {
        let oauth2 = OAuth2CodeGrant(settings: [
            "client_id": configuration.clientId,
            "client_secret": "",
            "authorize_uri": configuration.authorizeUri,
            "token_uri": configuration.tokenUri,
            "redirect_uris": configuration.redirectUris,
            "scope": configuration.scope,
            "parameters": configuration.customParameters,
            "secret_in_body": false,
            "keychain": true,
            "use_pkce": true,
        ] as OAuth2JSON)
        oauth2.logger = OAuth2DebugLogger(configuration.logLevel)
        oauth2.afterAuthorizeOrFail = { [unowned self] authParameters, error in
            self.accessToken = oauth2.accessToken
        }
        self.oauth2 = oauth2
    }

    // MARK: Methods
    public func checkToken() {
        if let hasUnexpiredAccessToken = self.oauth2?.hasUnexpiredAccessToken(), hasUnexpiredAccessToken {
            self.oauth2?.doRefreshToken(callback: { [unowned self] authParameters, error in
                self.accessToken = self.oauth2?.accessToken
            })
        }
    }

    public func setupWhenUIIsReady() {
        if let oauth2 = self.oauth2 {
#if os(iOS)
            let rootViewController = (UIApplication.shared.connectedScenes.first as? UIWindowScene)?.windows.first?.rootViewController
#elseif os(macOS)
            let rootViewController = NSApplication.shared.windows.first?.windowController
#endif

            if rootViewController != nil {
                oauth2.authConfig.authorizeEmbedded = true
                oauth2.authConfig.authorizeContext = rootViewController
                oauth2.authConfig.ui.useSafariView = false
                oauth2.authConfig.ui.useAuthenticationSession = true
                oauth2.authConfig.ui.prefersEphemeralWebBrowserSession = true
            }
        }
    }

    public func authorize() {
        if let oauth2 = self.oauth2 {
            oauth2.authorize() { authParameters, error in
                if let error = error {
                    print("Authorization was canceled or went wrong: \(error).")
                }
            }
        }
    }

    public func unauthorize() {
        signOutTokenFromServer()
        removeCookies()
        self.oauth2?.abortAuthorization()
        self.oauth2?.forgetTokens()
    }

    private func signOutTokenFromServer() {
        if let oauth2 = self.oauth2 {
            if let signOutUriString = oauth2.clientConfig.customParameters?.first(where: { $0.key == "signOutUri" })?.value {
                var req = oauth2.request(forURL: URL(string: signOutUriString)!, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData)
                req.httpMethod = "POST"
                let task = oauth2.session.dataTask(with: req) { data, response, error in
                    if let response = response as? HTTPURLResponse, response.statusCode == 204 {
                        print("Sign out from server.")
                    } else if let error = error {
                        print(error)
                    }
                }
                task.resume()
            }
        }
    }

    private func removeCookies() {
        HTTPCookieStorage.shared.removeCookies(since: Date.distantPast)
        print("All cookies deleted.")

        WKWebsiteDataStore.default().fetchDataRecords(ofTypes: WKWebsiteDataStore.allWebsiteDataTypes()) { records in
            records.forEach { record in
                WKWebsiteDataStore.default().removeData(ofTypes: record.dataTypes, for: [record], completionHandler: {})
                print("Cookie \(record) deleted.")
            }
        }
    }
}
rushairer commented 1 year ago

in completionHandler got this error:

Printing description of error:
▿ Optional<Error>
  - some : Error Domain=com.apple.AuthenticationServices.WebAuthenticationSession Code=2 "Cannot start ASWebAuthenticationSession without providing presentation context. Set presentationContextProvider before calling -start." UserInfo={NSDebugDescription=Cannot start ASWebAuthenticationSession without providing presentation context. Set presentationContextProvider before calling -start.}