Compare commits

...
Author SHA1 Message Date
superdooper86 b8826ace9b fix: NSNumber cast and use .page world in callAsyncJavaScript auth check
callAsyncJavaScript returns JS numbers as NSNumber (Double-backed).
'val as? Int' silently returns nil for 200.0, so onAuthenticated never
fired. Fixed with 'val as? NSNumber then .intValue == 200'.

Also switched content world from .defaultClient to .page so the fetch
runs in the same JS context as the loaded page.
2026-05-11 10:22:59 +02:00
github-actions[bot] 2a0de269ff Beta release v1.2.1-beta.8 2026-05-11 08:07:51 +00:00
superdooper86 ac7ffe81af 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.
2026-05-11 10:06:33 +02:00
github-actions[bot] f28f7b8a5a Beta release v1.2.1-beta.7 2026-05-11 07:51:25 +00:00
6 changed files with 28 additions and 18 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.9</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>55</string> <string>57</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
<string>13.0</string> <string>13.0</string>
<key>LSUIElement</key> <key>LSUIElement</key>
+15 -7
View File
@@ -36,13 +36,21 @@ 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: .page
self.onAuthenticated() ) { [weak self] result in
guard let self, !self.didAuthenticate else { return }
// JS numbers arrive as NSNumber (Double-backed), not Swift Int
if case .success(let val) = result,
let n = val as? NSNumber, n.intValue == 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 {
+1 -1
View File
@@ -10,7 +10,7 @@
[![Swift](https://img.shields.io/badge/Swift-5.9-F05138?style=flat&logo=swift&logoColor=white)](https://swift.org) [![Swift](https://img.shields.io/badge/Swift-5.9-F05138?style=flat&logo=swift&logoColor=white)](https://swift.org)
[![Version](https://img.shields.io/badge/version-1.2.0-orange?style=flat)](https://github.com/superdooper86/claudechecker/releases) [![Version](https://img.shields.io/badge/version-1.2.0-orange?style=flat)](https://github.com/superdooper86/claudechecker/releases)
[![License](https://img.shields.io/badge/license-MIT-blue?style=flat)](LICENSE) [![License](https://img.shields.io/badge/license-MIT-blue?style=flat)](LICENSE)
[![Beta](https://img.shields.io/badge/beta-1.2.1--beta.6-orange?style=flat)](https://github.com/superdooper86/claudechecker/releases/tag/v1.2.1-beta.6) <!-- BETA_BADGE --> [![Beta](https://img.shields.io/badge/beta-1.2.1--beta.8-orange?style=flat)](https://github.com/superdooper86/claudechecker/releases/tag/v1.2.1-beta.8) <!-- BETA_BADGE -->
</div> </div>
+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
+3 -3
View File
@@ -1,5 +1,5 @@
{ {
"version": "1.2.1-beta.6", "version": "1.2.1-beta.8",
"url": "https://github.com/superdooper86/claudechecker/releases/download/v1.2.1-beta.6/ClaudeChecker.zip", "url": "https://github.com/superdooper86/claudechecker/releases/download/v1.2.1-beta.8/ClaudeChecker.zip",
"notes": "## What's new in v1.2.1\n\n### Bug fixes\n- Fixed \"Not signed in\" showing incorrectly on launch when the session was already active\n- Sign-in state is now detected immediately from stored cookies on startup, before the first data refresh completes\n- Fixed login window auto-closing before the user could sign in — the login window now correctly loads the `/login` page so it only detects auth after the actual sign-in redirect\n- Fixed \"No API key configured\" showing after signing out — now correctly shows \"Not signed in\" with a prompt to sign in\n- Added `/api/organizations` as a final fallback for org ID resolution when the bootstrap API response doesn't include it\n- Fixed usage data not loading after sign-in — API requests now include required browser-like headers (Origin, Referer, User-Agent) that Claude's usage endpoints require" "notes": "## What's new in v1.2.1\n\n### Bug fixes\n- Fixed \"Not signed in\" showing incorrectly on launch when the session was already active\n- Sign-in state is now detected immediately from stored cookies on startup, before the first data refresh completes\n- Fixed login window auto-closing before the user could sign in — the login window now correctly loads the `/login` page so it only detects auth after the actual sign-in redirect\n- Fixed \"No API key configured\" showing after signing out — now correctly shows \"Not signed in\" with a prompt to sign in\n- Added `/api/organizations` as a final fallback for org ID resolution when the bootstrap API response doesn't include it\n- Fixed usage data not loading — API requests now include required browser-like headers (Origin, Referer, User-Agent)\n- Fixed Settings incorrectly showing \"Signed in\" after a failed refresh — sign-in state now resets when authentication fails\n- 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\n- Fixed API requests not including session cookies — now sends all cookies from the app's WebView store rather than filtering by domain"
} }