Эх сурвалжийг харах

Fix Zoom Production OAuth 4,700 by switching to HTTPS callback.

Zoom Production rejects loopback redirects (http://127.0.0.1:8742/oauth2redirect)
even with Public Client PKCE and a matching allow list, while Development accepts
the same URI. Sign-in now uses https://www.mqldevelopment.com/oauth/zoom-callback
with the Public Client ID + PKCE; the in-app WKWebView intercepts that HTTPS
callback when Zoom returns the authorization code.

Also update App Store / Marketplace reviewer notes for the new redirect, document
that Marketplace’s generated OAuth URL embeds the confidential Client ID (do not
copy it), and add marketplace upload assets including an optional HTTPS landing
page (oauth-zoom-callback.html).

Co-authored-by: Cursor <cursoragent@cursor.com>
Uzair Tahir 3 сар өмнө
parent
commit
a5717156ce

+ 6 - 3
.env.example

@@ -32,9 +32,12 @@ APPLE_ID=
 APP_SPECIFIC_PASSWORD=
 
 # Zoom Marketplace OAuth app
-# Redirect URI allow list must include: http://127.0.0.1:8742/oauth2redirect
-# Mirror these in the Xcode scheme (Run → Arguments → Environment Variables) for local runs.
-# With Use Public Client OAuth ON, set ZOOM_OAUTH_PUBLIC_CLIENT_ID for PKCE loopback sign-in.
+# Production redirect URI / allow list must include exactly:
+#   https://www.mqldevelopment.com/oauth/zoom-callback
+# (Production rejects loopback http://127.0.0.1:8742/oauth2redirect with error 4,700.)
+# Mirror Client ID values in the Xcode scheme (Run → Arguments → Environment Variables).
+# With Use Public Client OAuth ON, authorize uses ZOOM_OAUTH_PUBLIC_CLIENT_ID + PKCE.
+# Do not copy Marketplace’s generated OAuth URL — it uses the confidential Client ID and fails with loopback/HTTPS PKCE mismatch.
 ZOOM_OAUTH_CLIENT_ID=
 ZOOM_OAUTH_PUBLIC_CLIENT_ID=
 ZOOM_OAUTH_CLIENT_SECRET=

+ 2 - 2
appconnect/US/notes.txt

@@ -8,7 +8,7 @@ How to review:
 • Path: Sign in with Zoom → Premium → Home/Meetings/Scheduler → menu bar Join or Schedule → embedded Zoom → optional “Record locally?” → AI Companion (transcribe, Generate notes) → Widgets → Settings reminders → Sign out.
 
 External services:
-• Zoom — OAuth (http://127.0.0.1:8742/oauth2redirect), APIs, embedded WKWebView join.
+• Zoom — OAuth (https://www.mqldevelopment.com/oauth/zoom-callback, Public Client + PKCE), APIs, embedded WKWebView join.
 • OpenAI — notes generation from transcript only, on user action.
 • Apple — StoreKit, Speech, UserNotifications, WebKit, ScreenCaptureKit, AVFoundation.
 
@@ -25,7 +25,7 @@ Required for Mac App Store distribution. Restricts filesystem and OS access; oth
 Outbound HTTPS to Zoom APIs and web hosts, OpenAI (notes generation), and Apple services (StoreKit, etc.).
 
 • com.apple.security.network.server
-Localhost listener on 127.0.0.1:8742 only, to complete Zoom OAuth when the authorization redirect returns to http://127.0.0.1:8742/oauth2redirect. Not exposed on the public internet; used during sign-in.
+Localhost listener on 127.0.0.1:8742 only (legacy/local OAuth helpers). Zoom Production sign-in uses HTTPS callback interception in WKWebView. Not exposed on the public internet.
 
 • com.apple.security.device.audio-input
 Microphone for embedded Zoom meetings and for AI Companion recordings when the user starts or allows recording.

+ 83 - 0
appconnect/marketplace-assets/README-UPLOAD.txt

@@ -0,0 +1,83 @@
+Zoom Marketplace Production — remaining manual steps
+====================================================
+
+App: Companion AI for Zoom (MQL Development Ltd)
+Marketplace ID: PnrZvMHcQwWDeZGpe9Wn6Q
+
+Status as of last agent pass
+----------------------------
+DONE (in Marketplace UI):
+• Production Basic Info — Public Client OAuth + PKCE + HTTPS redirect
+  (`https://www.mqldevelopment.com/oauth/zoom-callback`)
+• Production Scopes — meeting list/write/summary, user:read:zak
+• App Listing text — name, company, short/long description, category
+• Link & Support URLs + data-subject-rights checkbox
+• Technical Design → Security (3/3)
+• Technical Design → Overview tech stack text + SSDLC/SAST/pen-test = No
+• EU & Discoverability — business name/address/email/phone (+44)/trade register 15088000 + union-law declaration
+• Mac app code — Production OAuth credentials + PKCE + HTTPS callback interception
+
+Host on www.mqldevelopment.com (optional polish):
+• Upload `oauth-zoom-callback.html` to path `/oauth/zoom-callback` so the HTTPS landing page is not a 404.
+  OAuth still works without it — the Mac app intercepts the redirect URL in WKWebView.
+
+NOT DONE (must upload in browser — Cursor cannot attach local files):
+1) Technical Design → Overview → Architecture Diagram
+   File: architecture-diagram.png
+   After upload, Overview should reach 5/5. Click Continue to save.
+
+2) App Listing → App Information → images
+   • App icon light + dark: app-icon-256.png (or app-icon-512.png)
+   • Cover: cover-1824x176.png (exactly 1824×176)
+   • Gallery: gallery-1200x780.png (replace with real screenshots when ready)
+
+3) EU & Discoverability — “Available in the EU” is ON. Still need from you:
+   • Last 4 digits of business bank account
+   • Bank name
+   • Identification document (Companies House incorporation cert / articles / similar)
+   Then click Continue.
+   (Already filled: MQL name, address, info@mqldevelopment.com, +44 7456277321, trade register 15088000, declaration.)
+
+4) Link & Support — re-confirm data-rights checkbox if Continue did not persist.
+
+5) Publish → Submit for review once checklist is clear.
+
+Publish page currently reports missing:
+• App Listing - App Information (3) → icons + cover/gallery uploads
+• App Listing - EU & Discoverability (4) → bank last4, bank name, ID doc (+ ensure declaration saved)
+• Technical Design (1) → architecture diagram upload
+
+
+Prepared files in this folder
+-----------------------------
+app-icon-256.png / app-icon-512.png
+cover-1824x176.png
+gallery-1200x780.png
+architecture-diagram.png
+
+
+Company details used
+--------------------
+Company: MQL Development Ltd
+Company number: 15088000 (UK)
+Address: 7 Bell Yard, London, WC2A 2JR, United Kingdom
+Website: https://www.mqldevelopment.com
+Support email: info@mqldevelopment.com
+Phone: +44 7456 277321
+
+
+URLs (Link & Support)
+---------------------
+Privacy: https://sites.google.com/view/aicompanionforzoom/privacy-policy
+Terms:   https://sites.google.com/view/aicompanionforzoom/terms-and-condition
+Support: https://sites.google.com/view/aicompanionforzoom/get-support
+Docs:    https://sites.google.com/view/aicompanionforzoom/home
+Company: https://www.mqldevelopment.com
+
+
+Notes
+-----
+• “Ready for beta test” + Production keys allows TestFlight / Zoom tester accounts.
+• Full public Zoom Marketplace / all-user Production OAuth usually needs Marketplace publication (or Unlisted + review approval).
+• Gallery image is a placeholder — swap for real product screenshots before public listing.
+• If you later answer Yes to SSDLC / SAST / pen-test, upload evidence documents on Technical Design → Overview.

BIN
appconnect/marketplace-assets/app-icon-256.png


BIN
appconnect/marketplace-assets/app-icon-512.png


BIN
appconnect/marketplace-assets/architecture-diagram.png


BIN
appconnect/marketplace-assets/cover-1824x176.png


BIN
appconnect/marketplace-assets/cover-source.png


BIN
appconnect/marketplace-assets/gallery-1200x780.png


+ 39 - 0
appconnect/marketplace-assets/oauth-zoom-callback.html

@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="utf-8" />
+  <meta name="viewport" content="width=device-width, initial-scale=1" />
+  <title>Zoom sign-in complete</title>
+  <style>
+    body {
+      font-family: system-ui, -apple-system, sans-serif;
+      max-width: 32rem;
+      margin: 4rem auto;
+      padding: 0 1.25rem;
+      color: #1a1a1a;
+      line-height: 1.5;
+    }
+  </style>
+</head>
+<body>
+  <h1>Sign-in complete</h1>
+  <p>
+    You can return to <strong>Companion AI for Zoom</strong>. If the Mac app did not close the sign-in window,
+    close this tab and continue in the app.
+  </p>
+  <script>
+    // Optional fallback when OAuth runs outside the in-app web view:
+    // forward code/state to the local listener used for legacy loopback flows.
+    (function () {
+      try {
+        var q = window.location.search || "";
+        if (!q) return;
+        var loopback = "http://127.0.0.1:8742/oauth2redirect" + q;
+        // Prefer staying on HTTPS for the in-app WKWebView intercept path.
+        // Uncomment to also attempt loopback handoff for external browsers:
+        // window.location.replace(loopback);
+      } catch (e) {}
+    })();
+  </script>
+</body>
+</html>

+ 31 - 0
appconnect/marketplace-assets/zoom-reviewer-release-notes.txt

@@ -0,0 +1,31 @@
+APP OVERVIEW
+Companion AI for Zoom is a native macOS desktop app (Mac App Store) by MQL Development Ltd. It is NOT a Zoom App that runs inside the Zoom client meeting UI. Users authorize this Marketplace OAuth app so the Mac app can call Zoom REST APIs and embed meetings in WKWebView.
+
+OAUTH / INTEGRATION
+• App type: User-managed OAuth app (Production)
+• Public Client OAuth + PKCE enabled
+• Redirect URI (allow list): https://www.mqldevelopment.com/oauth/zoom-callback
+• In-app WKWebView intercepts the HTTPS OAuth callback during sign-in (Public Client + PKCE)
+• Scopes requested:
+  - meeting:read:list_meetings — list upcoming/past meetings
+  - meeting:write:meeting — schedule / start meetings
+  - meeting:read:summary — load Zoom meeting summaries when available
+  - user:read:zak — ZAK for embedded web join
+• Tokens stored locally on the Mac (Keychain / app sandbox). No Zoom webhook subscriptions.
+• Optional: OpenAI used only when the user taps Generate notes on a local transcript (not for OAuth).
+
+HOW TO TEST (Zoom Marketplace review)
+1. Install the production/TestFlight macOS build we provide under “Test device and environment → macOS”.
+2. Launch the app → Sign in with Zoom (complete OAuth consent for the scopes above).
+3. Use a Zoom account that has at least one scheduled meeting.
+4. Verify Home / Meetings list loads via Zoom APIs.
+5. Open Scheduler → create or schedule a meeting (meeting:write:meeting).
+6. Join a meeting from the app (embedded Zoom web join using ZAK when granted).
+7. Optional: allow local recording after join → AI Companion → transcribe on-device → Generate notes.
+8. Sign out and confirm tokens are cleared.
+
+EXPECTED BEHAVIOR
+After consent, the Mac app lists meetings, can schedule, join via embedded web UI, and may read meeting summaries. Premium gates some non-Zoom UI features (widgets/reminders) via Apple IAP; Zoom OAuth itself does not require Premium.
+
+CONTACT
+MQL Development Ltd — info@mqldevelopment.com — https://www.mqldevelopment.com

+ 10 - 4
zoom_app/ViewController.swift

@@ -10806,6 +10806,11 @@ final class ZoomOAuthTokenStore {
 
 final class ZoomOAuthService: NSObject {
     static let shared = ZoomOAuthService()
+
+    /// Production Marketplace reject loopback (4,700). HTTPS callback is intercepted by the in-app WKWebView.
+    /// Must match Marketplace → Production → OAuth Redirect URL / Allow List exactly.
+    static let zoomOAuthRedirectURI = "https://www.mqldevelopment.com/oauth/zoom-callback"
+
     private var inAppOAuthWindowController: InAppOAuthWindowController?
 
     /// When set, Zoom OAuth runs in this session instead of a separate window (unused; sign-in uses `InAppOAuthWindowController`).
@@ -10828,7 +10833,7 @@ final class ZoomOAuthService: NSObject {
     // Optional: put OAuth app credentials here for local-only testing (do not ship secrets in release builds).
     /// Confidential Client ID (token refresh). Production Marketplace value — used by Archive/App Store builds.
     private let bundledClientId = "X4fHO95lTdWGvmvQYGeQEA"
-    /// Public Client ID for loopback authorize + PKCE (Production Marketplace value).
+    /// Public Client ID for authorize + PKCE (Production Marketplace value).
     private let bundledPublicClientId = "bCMJ3KHVR8K1SkZC5ubmXg"
     /// Prefer `ZOOM_OAUTH_CLIENT_SECRET` env or UserDefaults when distributing; rotate if this value is ever leaked.
     private let bundledClientSecret = "yG75pEEVsQ94GgqN8j2EBdv3WaD57m5c"
@@ -10953,10 +10958,11 @@ final class ZoomOAuthService: NSObject {
 
     private func interactiveSignIn(presentingWindow: NSWindow?) async throws -> ZoomOAuthTokens {
         guard let presentingWindow else { throw ZoomOAuthError.unableToOpenBrowser }
-        // Loopback redirect requires Public Client OAuth + PKCE (not confidential Client ID alone).
+        // Production loopback is rejected by Zoom (4,700). Use HTTPS callback + Public Client PKCE.
+        // The in-app WKWebView intercepts this URL prefix when Zoom redirects with ?code=...
         guard let publicClientId = configuredPublicClientId() else { throw ZoomOAuthError.missingPublicClientId }
 
-        let redirectURI = "http://127.0.0.1:8742/oauth2redirect"
+        let redirectURI = Self.zoomOAuthRedirectURI
         let state = UUID().uuidString
         let codeVerifier = Self.randomURLSafeString(length: 64)
         let codeChallenge = Self.pkceChallenge(for: codeVerifier)
@@ -11569,7 +11575,7 @@ extension ZoomOAuthError: LocalizedError {
         case .missingClientSecret:
             return "Zoom OAuth Client Secret is not set (ZOOM_OAUTH_CLIENT_SECRET env, UserDefaults, or the setup prompt)."
         case .invalidCallbackURL:
-            return "The OAuth redirect URL was invalid. In your Zoom app OAuth allow list, add exactly http://127.0.0.1:8742/oauth2redirect. With Public Client OAuth ON, authorize must use the Public Client ID and PKCE."
+            return "The OAuth redirect URL was invalid. In Marketplace → Production OAuth Allow List, add exactly \(ZoomOAuthService.zoomOAuthRedirectURI). Sign-in must use the Public Client ID with PKCE (not the confidential Client ID from Marketplace’s generated OAuth URL)."
         case .missingAuthorizationCode:
             return "Zoom did not return an authorization code."
         case .tokenExchangeFailed(let details):