fix: use WebView JS fetch for auth detection; send all cookies to API

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.
This commit is contained in:
superdooper86
2026-05-11 10:06:33 +02:00
parent f28f7b8a5a
commit ac7ffe81af
4 changed files with 22 additions and 14 deletions
+2 -2
View File
@@ -15,9 +15,9 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.2.1-beta.7</string> <string>1.2.1-beta.8</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>55</string> <string>56</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
<string>13.0</string> <string>13.0</string>
<key>LSUIElement</key> <key>LSUIElement</key>
+13 -7
View File
@@ -36,13 +36,19 @@ struct LoginWebView: NSViewRepresentable {
if let url = webView.url?.absoluteString, if let url = webView.url?.absoluteString,
url.contains("/login") || url.contains("/auth") { return } 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 // Ask the WebView itself whether we're authenticated it uses its own
WKWebsiteDataStore.default().httpCookieStore.getAllCookies { cookies in // session (cookies, localStorage, etc.) so we don't need to know the
let hasAnyCookie = cookies.contains { $0.domain.contains("claude.ai") || $0.domain.contains("anthropic.com") } // cookie domain or name.
guard hasAnyCookie, !self.didAuthenticate else { return } webView.callAsyncJavaScript(
self.didAuthenticate = true "const r = await fetch('/api/bootstrap', {credentials: 'include'}); return r.status;",
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { arguments: [:], in: nil, in: .defaultClient
self.onAuthenticated() ) { [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()
}
} }
} }
} }
+5 -4
View File
@@ -43,8 +43,7 @@ class UsageViewModel: ObservableObject {
private func checkInitialSignInState() async { private func checkInitialSignInState() async {
let cookies = await WKWebsiteDataStore.default().httpCookieStore.allCookies() let cookies = await WKWebsiteDataStore.default().httpCookieStore.allCookies()
let hasAnyCookie = cookies.contains { $0.domain.contains("claude.ai") || $0.domain.contains("anthropic.com") } if !cookies.isEmpty { isSignedIn = true }
if hasAnyCookie { isSignedIn = true }
} }
func signOut() async { func signOut() async {
@@ -193,8 +192,10 @@ class UsageViewModel: ObservableObject {
private func claudeCookieHeader() async -> String? { private func claudeCookieHeader() async -> String? {
let cookies = await WKWebsiteDataStore.default().httpCookieStore.allCookies() let cookies = await WKWebsiteDataStore.default().httpCookieStore.allCookies()
let claudeCookies = cookies.filter { $0.domain.contains("claude.ai") || $0.domain.contains("anthropic.com") } guard !cookies.isEmpty else { return nil }
return HTTPCookie.requestHeaderFields(with: claudeCookies)["Cookie"] // 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 { private func fetchUsage(orgId: String) async throws -> UsageResponse {
+2 -1
View File
@@ -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 - 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 - 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 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 - 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