|
|
@@ -460,8 +460,10 @@ class ViewController: NSViewController {
|
|
|
private var embeddedBrowserMainColumnCollapsedWidthConstraint: NSLayoutConstraint?
|
|
|
private var oauthSessionContinuation: CheckedContinuation<URL, Error>?
|
|
|
private var oauthSessionCallbackPrefix: String?
|
|
|
- /// ZAK + display name for the next embedded join; used to re-auth the web client if the guest name screen appears.
|
|
|
+ /// ZAK + display name for the next embedded join; used to re-auth the web client if sign-in or guest join appears.
|
|
|
private var embeddedZoomPendingWebAuth: (zak: String, displayName: String?)?
|
|
|
+ /// Normalized `/wc/join/` URL for the in-flight embedded join (used to recover from web sign-in redirects).
|
|
|
+ private var embeddedZoomPendingJoinURL: URL?
|
|
|
private var zoomSignedInDisplayName: String?
|
|
|
/// Gap between main column and right web panel (must match Auto Layout constant).
|
|
|
private let homeEmbeddedBrowserColumnGap: CGFloat = 10
|
|
|
@@ -2168,6 +2170,7 @@ class ViewController: NSViewController {
|
|
|
@MainActor
|
|
|
private func primeZoomEmbeddedWebSession() {
|
|
|
guard isUserLoggedIn(), let webView = embeddedBrowserWebView else { return }
|
|
|
+ guard embeddedZoomPendingJoinURL == nil, isEmbeddedBrowserPanelOpen == false else { return }
|
|
|
guard let homeURL = URL(string: "https://app.zoom.us/wc/home") else { return }
|
|
|
webView.load(URLRequest(url: homeURL))
|
|
|
}
|
|
|
@@ -2185,7 +2188,10 @@ class ViewController: NSViewController {
|
|
|
@MainActor
|
|
|
private func fetchEmbeddedZoomZAKAllowingReConsent() async -> String? {
|
|
|
do {
|
|
|
- let accessToken = try await zoomOAuth.validAccessToken(presentingWindow: view.window)
|
|
|
+ let accessToken = try await zoomOAuth.validAccessToken(
|
|
|
+ presentingWindow: view.window,
|
|
|
+ requiringAnyOf: ["user_zak:read"]
|
|
|
+ )
|
|
|
return try await fetchZoomUserZAK(accessToken: accessToken)
|
|
|
} catch {
|
|
|
if case ZoomOAuthError.authenticationCancelled = error { return nil }
|
|
|
@@ -2226,7 +2232,13 @@ class ViewController: NSViewController {
|
|
|
@MainActor
|
|
|
private func embeddedZoomMeetingLoadURL(for url: URL) async -> URL {
|
|
|
let normalized = zoomEmbeddedWebHostURL(for: url)
|
|
|
- guard isZoomMeetingJoinOrStartURL(normalized), isUserLoggedIn() else { return normalized }
|
|
|
+ guard isZoomMeetingJoinOrStartURL(normalized), isUserLoggedIn() else {
|
|
|
+ embeddedZoomPendingJoinURL = nil
|
|
|
+ embeddedZoomPendingWebAuth = nil
|
|
|
+ return normalized
|
|
|
+ }
|
|
|
+
|
|
|
+ embeddedZoomPendingJoinURL = normalized
|
|
|
|
|
|
if let existingZAK = zakQueryValueFromZoomURL(normalized) {
|
|
|
let displayName = zoomSignedInDisplayName
|
|
|
@@ -2234,8 +2246,6 @@ class ViewController: NSViewController {
|
|
|
return zoomMeetingURLByAppendingWebAuth(normalized, zak: existingZAK, displayName: displayName)
|
|
|
}
|
|
|
|
|
|
- primeZoomEmbeddedWebSession()
|
|
|
-
|
|
|
if let zak = await fetchEmbeddedZoomZAKAllowingReConsent() {
|
|
|
let displayName = zoomSignedInDisplayName
|
|
|
embeddedZoomPendingWebAuth = (zak: zak, displayName: displayName)
|
|
|
@@ -2247,14 +2257,48 @@ class ViewController: NSViewController {
|
|
|
return zoomMeetingURLWithSignedInDisplayNameFallback(normalized)
|
|
|
}
|
|
|
|
|
|
+ private func embeddedWebViewShowsUnauthenticatedJoinPrompt(webView: WKWebView) -> Bool {
|
|
|
+ let title = webView.title ?? ""
|
|
|
+ if title.localizedCaseInsensitiveContains("Enter Meeting Info")
|
|
|
+ || title.localizedCaseInsensitiveContains("Enter your name") {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ if title.localizedCaseInsensitiveContains("sign in") {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ if let path = webView.url?.path.lowercased(), path.hasPrefix("/signin") {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
@MainActor
|
|
|
private func applyEmbeddedZoomWebAuthenticationIfNeeded(webView: WKWebView) {
|
|
|
+ guard isUserLoggedIn() else { return }
|
|
|
+ guard embeddedWebViewShowsUnauthenticatedJoinPrompt(webView: webView)
|
|
|
+ || (webView.url.map { isZoomMeetingJoinOrStartURL($0) } ?? false) else { return }
|
|
|
+
|
|
|
+ if let pending = embeddedZoomPendingWebAuth,
|
|
|
+ let joinURL = embeddedZoomPendingJoinURL {
|
|
|
+ let authURL = zoomMeetingURLByAppendingWebAuth(joinURL, zak: pending.zak, displayName: pending.displayName)
|
|
|
+ if webView.url?.absoluteString != authURL.absoluteString {
|
|
|
+ webView.load(URLRequest(url: authURL))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ } else if embeddedZoomPendingJoinURL != nil {
|
|
|
+ Task { @MainActor in
|
|
|
+ guard self.embeddedBrowserWebView === webView else { return }
|
|
|
+ guard let zak = await self.fetchEmbeddedZoomZAKAllowingReConsent() else { return }
|
|
|
+ self.embeddedZoomPendingWebAuth = (zak: zak, displayName: self.zoomSignedInDisplayName)
|
|
|
+ self.applyEmbeddedZoomWebAuthenticationIfNeeded(webView: webView)
|
|
|
+ }
|
|
|
+ return
|
|
|
+ } else {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
guard let pending = embeddedZoomPendingWebAuth else { return }
|
|
|
guard let pageURL = webView.url, isZoomMeetingJoinOrStartURL(pageURL) else { return }
|
|
|
- let title = webView.title ?? ""
|
|
|
- let onGuestJoinScreen = title.localizedCaseInsensitiveContains("Enter Meeting Info")
|
|
|
- || title.localizedCaseInsensitiveContains("Enter your name")
|
|
|
- guard onGuestJoinScreen else { return }
|
|
|
|
|
|
let zakLiteral = javaScriptStringLiteral(pending.zak)
|
|
|
let nameLiteral = pending.displayName.map(javaScriptStringLiteral) ?? "null"
|
|
|
@@ -2810,6 +2854,7 @@ class ViewController: NSViewController {
|
|
|
teardownEmbeddedZoomWebClient()
|
|
|
}
|
|
|
embeddedZoomPendingWebAuth = nil
|
|
|
+ embeddedZoomPendingJoinURL = nil
|
|
|
embeddedBrowserPanelWidthConstraint?.constant = 0
|
|
|
embeddedBrowserMainColumnCollapsedWidthConstraint?.isActive = false
|
|
|
homeMainContentColumnView?.isHidden = false
|
|
|
@@ -2893,6 +2938,7 @@ class ViewController: NSViewController {
|
|
|
zoomOAuth.clearSavedTokens()
|
|
|
zoomSignedInDisplayName = nil
|
|
|
embeddedZoomPendingWebAuth = nil
|
|
|
+ embeddedZoomPendingJoinURL = nil
|
|
|
persistLoggedInState(false)
|
|
|
allScheduledMeetings = []
|
|
|
applyAuthenticationUIState(triggerInitialRefresh: false)
|
|
|
@@ -10377,6 +10423,7 @@ extension ViewController: WKNavigationDelegate, WKUIDelegate {
|
|
|
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
|
|
|
guard webView === embeddedBrowserWebView else { return }
|
|
|
updateEmbeddedMeetingRecordingPresence(for: webView.url, webViewTitle: webView.title)
|
|
|
+ applyEmbeddedZoomWebAuthenticationIfNeeded(webView: webView)
|
|
|
refreshEmbeddedBrowserNavButtons()
|
|
|
}
|
|
|
|
|
|
@@ -10386,6 +10433,7 @@ extension ViewController: WKNavigationDelegate, WKUIDelegate {
|
|
|
applyEmbeddedZoomWebAuthenticationIfNeeded(webView: webView)
|
|
|
if let pageURL = webView.url, isZoomEmbeddedWebClientInCallURL(pageURL) {
|
|
|
embeddedZoomPendingWebAuth = nil
|
|
|
+ embeddedZoomPendingJoinURL = nil
|
|
|
}
|
|
|
refreshEmbeddedBrowserNavButtons()
|
|
|
}
|