Selaa lähdekoodia

Fix embedded web join showing Sign in when app is logged in.

Stop priming /wc/home during join, recover with ZAK on sign-in and guest screens, and require user_zak:read for embedded meeting auth.

Co-authored-by: Cursor <cursoragent@cursor.com>
huzaifahayat12 2 kuukautta sitten
vanhempi
sitoutus
bebe8d85ab
1 muutettua tiedostoa jossa 57 lisäystä ja 9 poistoa
  1. 57 9
      zoom_app/ViewController.swift

+ 57 - 9
zoom_app/ViewController.swift

@@ -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()
     }