From ac7ffe81afd035c29934610a6f105d956a48a9d9 Mon Sep 17 00:00:00 2001 From: superdooper86 Date: Mon, 11 May 2026 10:06:33 +0200 Subject: [PATCH] fix: use WebView JS fetch for auth detection; send all cookies to API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cookie domain filtering was wrong — the session token domain is unknown and was never found by claude.ai/anthropic.com filters. LoginView: replace getAllCookies domain check with callAsyncJavaScript that fetches /api/bootstrap directly from the WebView. The WebView uses its own full session (all cookies, any domain) so auth is detected correctly regardless of where the token lives. UsageViewModel: claudeCookieHeader now sends all cookies from the app's WKWebsiteDataStore instead of filtering by domain. checkInitialSignInState likewise checks for any cookie. --- ClaudeChecker/Info.plist | 4 ++-- ClaudeChecker/LoginView.swift | 20 +++++++++++++------- ClaudeChecker/UsageViewModel.swift | 9 +++++---- RELEASE_NOTES.md | 3 ++- 4 files changed, 22 insertions(+), 14 deletions(-) diff --git a/ClaudeChecker/Info.plist b/ClaudeChecker/Info.plist index 5ae3940..9d6db50 100644 --- a/ClaudeChecker/Info.plist +++ b/ClaudeChecker/Info.plist @@ -15,9 +15,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.2.1-beta.7 + 1.2.1-beta.8 CFBundleVersion - 55 + 56 LSMinimumSystemVersion 13.0 LSUIElement diff --git a/ClaudeChecker/LoginView.swift b/ClaudeChecker/LoginView.swift index 26830cb..4176f4e 100644 --- a/ClaudeChecker/LoginView.swift +++ b/ClaudeChecker/LoginView.swift @@ -36,13 +36,19 @@ struct LoginWebView: NSViewRepresentable { if let url = webView.url?.absoluteString, url.contains("/login") || url.contains("/auth") { return } - // URL is not a login/auth page, so if any claude.ai cookie exists we're signed in - WKWebsiteDataStore.default().httpCookieStore.getAllCookies { cookies in - let hasAnyCookie = cookies.contains { $0.domain.contains("claude.ai") || $0.domain.contains("anthropic.com") } - guard hasAnyCookie, !self.didAuthenticate else { return } - self.didAuthenticate = true - DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { - self.onAuthenticated() + // Ask the WebView itself whether we're authenticated — it uses its own + // session (cookies, localStorage, etc.) so we don't need to know the + // cookie domain or name. + webView.callAsyncJavaScript( + "const r = await fetch('/api/bootstrap', {credentials: 'include'}); return r.status;", + arguments: [:], in: nil, in: .defaultClient + ) { [weak self] result in + guard let self, !self.didAuthenticate else { return } + if case .success(let val) = result, let status = val as? Int, status == 200 { + self.didAuthenticate = true + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { + self.onAuthenticated() + } } } } diff --git a/ClaudeChecker/UsageViewModel.swift b/ClaudeChecker/UsageViewModel.swift index b96caf6..9ab091d 100644 --- a/ClaudeChecker/UsageViewModel.swift +++ b/ClaudeChecker/UsageViewModel.swift @@ -43,8 +43,7 @@ class UsageViewModel: ObservableObject { private func checkInitialSignInState() async { let cookies = await WKWebsiteDataStore.default().httpCookieStore.allCookies() - let hasAnyCookie = cookies.contains { $0.domain.contains("claude.ai") || $0.domain.contains("anthropic.com") } - if hasAnyCookie { isSignedIn = true } + if !cookies.isEmpty { isSignedIn = true } } func signOut() async { @@ -193,8 +192,10 @@ class UsageViewModel: ObservableObject { private func claudeCookieHeader() async -> String? { let cookies = await WKWebsiteDataStore.default().httpCookieStore.allCookies() - let claudeCookies = cookies.filter { $0.domain.contains("claude.ai") || $0.domain.contains("anthropic.com") } - return HTTPCookie.requestHeaderFields(with: claudeCookies)["Cookie"] + guard !cookies.isEmpty else { return nil } + // Send all cookies from the app's WebView store — the session token may be + // on any domain (claude.ai, anthropic.com, or an auth sub-service). + return HTTPCookie.requestHeaderFields(with: cookies)["Cookie"] } private func fetchUsage(orgId: String) async throws -> UsageResponse { diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index d3c4a36..4d7fbbf 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -7,5 +7,6 @@ - Fixed "No API key configured" showing after signing out — now correctly shows "Not signed in" with a prompt to sign in - Added `/api/organizations` as a final fallback for org ID resolution when the bootstrap API response doesn't include it - Fixed usage data not loading — API requests now include required browser-like headers (Origin, Referer, User-Agent) -- Fixed sign-in detection and cookie handling for accounts whose session cookies are on the `anthropic.com` domain rather than `claude.ai` - Fixed Settings incorrectly showing "Signed in" after a failed refresh — sign-in state now resets when authentication fails +- Rewrote login detection to use the WebView's own fetch call instead of inspecting cookie domains — correctly detects auth regardless of which domain the session token is stored on +- Fixed API requests not including session cookies — now sends all cookies from the app's WebView store rather than filtering by domain