|
|
@@ -19,20 +19,6 @@ private enum AppWindowMetrics {
|
|
|
static let defaultContentHeight: CGFloat = 690
|
|
|
}
|
|
|
|
|
|
-/// OAuth entry points stay in the system browser so enterprise SSO extensions and password managers behave as users expect.
|
|
|
-private enum InAppWebBrowserPolicy {
|
|
|
- static func shouldOpenInSystemBrowser(_ url: URL) -> Bool {
|
|
|
- guard let host = url.host?.lowercased() else { return false }
|
|
|
- let path = url.path.lowercased()
|
|
|
- let zoomHost = host == "zoom.us" || host.hasSuffix(".zoom.us") || host == "zoom.com" || host.hasSuffix(".zoom.com")
|
|
|
- if zoomHost, path.contains("/oauth/authorize") { return true }
|
|
|
- if host == "accounts.google.com" {
|
|
|
- return path.contains("/o/oauth2/") || path.contains("/signin/oauth")
|
|
|
- }
|
|
|
- return false
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
/// Errors from the main-window embedded OAuth web session (right panel).
|
|
|
private enum EmbeddedOAuthFlowError: Error {
|
|
|
case webViewUnavailable
|
|
|
@@ -7535,21 +7521,20 @@ final class ZoomOAuthService: NSObject {
|
|
|
URLQueryItem(name: "state", value: state)
|
|
|
]
|
|
|
guard let authURL = components.url else { throw ZoomOAuthError.invalidCallbackURL }
|
|
|
+ let oauthWindow = await MainActor.run { makeOAuthWindowController() }
|
|
|
do {
|
|
|
- _ = presentingWindow // keep signature stable; used for parity with Google flow + future UI alerts
|
|
|
- let listener = try OAuthRedirectListener(port: 8742, pathPrefix: "/oauth2redirect")
|
|
|
- guard NSWorkspace.shared.open(authURL) else {
|
|
|
- throw ZoomOAuthError.unableToOpenBrowser
|
|
|
- }
|
|
|
- let callbackURL = try await listener.waitForRedirect(timeoutSeconds: 180)
|
|
|
+ let callbackURL = try await oauthWindow.authenticate(
|
|
|
+ startURL: authURL,
|
|
|
+ callbackURLPrefix: redirectURI,
|
|
|
+ presentingWindow: presentingWindow,
|
|
|
+ timeoutSeconds: 180
|
|
|
+ )
|
|
|
let queryItems = URLComponents(url: callbackURL, resolvingAgainstBaseURL: false)?.queryItems
|
|
|
guard queryItems?.first(where: { $0.name == "state" })?.value == state else { throw ZoomOAuthError.invalidCallbackURL }
|
|
|
guard let code = queryItems?.first(where: { $0.name == "code" })?.value, code.isEmpty == false else {
|
|
|
throw ZoomOAuthError.missingAuthorizationCode
|
|
|
}
|
|
|
return try await exchangeCodeForTokens(code: code, redirectURI: redirectURI, clientId: clientId, clientSecret: clientSecret)
|
|
|
- } catch OAuthRedirectListenerError.timedOut {
|
|
|
- throw ZoomOAuthError.authenticationTimedOut
|
|
|
} catch GoogleOAuthError.authenticationTimedOut {
|
|
|
throw ZoomOAuthError.authenticationTimedOut
|
|
|
} catch GoogleOAuthError.authenticationCancelled {
|
|
|
@@ -8032,7 +8017,7 @@ extension ZoomOAuthError: LocalizedError {
|
|
|
}
|
|
|
return "Zoom rate limit reached. Try again later."
|
|
|
case .unableToOpenBrowser:
|
|
|
- return "Could not open the system browser for Zoom sign-in."
|
|
|
+ return "Could not open Zoom sign-in (no window to present from)."
|
|
|
case .authenticationTimedOut:
|
|
|
return "Zoom sign-in timed out waiting for the browser redirect."
|
|
|
case .authenticationCancelled:
|