From f6f999e32bcac73bbeb3e0fa0520b9a816f12e88 Mon Sep 17 00:00:00 2001 From: mertalev <101130780+mertalev@users.noreply.github.com> Date: Tue, 10 Mar 2026 12:26:58 -0500 Subject: [PATCH] handle network switching on logout --- .../alextran/immich/core/HttpClientManager.kt | 25 ++++++++++----- .../ios/Runner/Core/URLSessionManager.swift | 32 ++++++++++++------- 2 files changed, 38 insertions(+), 19 deletions(-) diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/core/HttpClientManager.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/core/HttpClientManager.kt index 9e3c1cc18e..bdec3a21df 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/core/HttpClientManager.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/core/HttpClientManager.kt @@ -17,7 +17,6 @@ import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.OkHttpClient import kotlinx.serialization.Serializable -import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import java.io.ByteArrayInputStream import java.io.File @@ -315,7 +314,7 @@ object HttpClientManager { val parsed = urls.mapNotNull { it.toHttpUrlOrNull() } if (parsed.map { it.host } == serverUrls.map { it.host }) return serverUrls = parsed - if (duplicateAuthCookies()) persist() + if (syncAuthCookies()) persist() } @Synchronized @@ -327,20 +326,30 @@ object HttpClientManager { cookies.any { it.name == existing.name && it.domain == existing.domain && it.path == existing.path } } store.addAll(cookies) - val duplicated = serverUrls.any { it.host == url.host } && duplicateAuthCookies() - if (changed || duplicated) persist() + val synced = serverUrls.any { it.host == url.host } && syncAuthCookies() + if (changed || synced) persist() } @Synchronized override fun loadForRequest(url: HttpUrl): List { val now = System.currentTimeMillis() - store.removeAll { it.expiresAt < now } + if (store.removeAll { it.expiresAt < now }) { + syncAuthCookies() + persist() + } return store.filter { it.matches(url) } } - private fun duplicateAuthCookies(): Boolean { - val sourceCookies = store.filter { it.name in AUTH_COOKIE_NAMES }.associateBy { it.name } - if (sourceCookies.isEmpty()) return false + private fun syncAuthCookies(): Boolean { + val serverHosts = serverUrls.map { it.host }.toSet() + val now = System.currentTimeMillis() + val sourceCookies = store + .filter { it.name in AUTH_COOKIE_NAMES && it.domain in serverHosts && it.expiresAt > now } + .associateBy { it.name } + + if (sourceCookies.isEmpty()) { + return store.removeAll { it.name in AUTH_COOKIE_NAMES && it.domain in serverHosts } + } var changed = false for (url in serverUrls) { diff --git a/mobile/ios/Runner/Core/URLSessionManager.swift b/mobile/ios/Runner/Core/URLSessionManager.swift index 9955c5d372..31b60bcb4e 100644 --- a/mobile/ios/Runner/Core/URLSessionManager.swift +++ b/mobile/ios/Runner/Core/URLSessionManager.swift @@ -3,7 +3,6 @@ import native_video_player let CLIENT_CERT_LABEL = "app.alextran.immich.client_identity" let HEADERS_KEY = "immich.request_headers" -let SERVER_URL_KEY = "immich.server_url" let SERVER_URLS_KEY = "immich.server_urls" let APP_GROUP = "group.app.immich.share" @@ -36,7 +35,7 @@ class URLSessionManager: NSObject { }() static let cookieStorage = HTTPCookieStorage.sharedCookieStorage(forGroupContainerIdentifier: APP_GROUP) private static var serverUrls: [String] = [] - private static var isDuplicating = false + private static var isSyncing = false var sessionPointer: UnsafeMutableRawPointer { Unmanaged.passUnretained(session).toOpaque() @@ -63,29 +62,40 @@ class URLSessionManager: NSObject { guard urls != serverUrls else { return } serverUrls = urls UserDefaults.group.set(urls, forKey: SERVER_URLS_KEY) - duplicateAuthCookies() + syncAuthCookies() } @objc private static func cookiesDidChange(_ notification: Notification) { - guard !isDuplicating, !serverUrls.isEmpty else { return } - duplicateAuthCookies() + guard !isSyncing, !serverUrls.isEmpty else { return } + syncAuthCookies() } - private static func duplicateAuthCookies() { + private static func syncAuthCookies() { let authCookieNames: Set = ["immich_access_token", "immich_is_authenticated", "immich_auth_type"] + let serverHosts = Set(serverUrls.compactMap { URL(string: $0)?.host }) let allCookies = cookieStorage.cookies ?? [] + let now = Date() + + let serverAuthCookies = allCookies.filter { + authCookieNames.contains($0.name) && serverHosts.contains($0.domain) + } var sourceCookies: [String: HTTPCookie] = [:] - for cookie in allCookies { - if authCookieNames.contains(cookie.name) { + for cookie in serverAuthCookies { + if cookie.expiresDate.map({ $0 > now }) ?? true { sourceCookies[cookie.name] = cookie } } - guard !sourceCookies.isEmpty else { return } + isSyncing = true + defer { isSyncing = false } - isDuplicating = true - defer { isDuplicating = false } + if sourceCookies.isEmpty { + for cookie in serverAuthCookies { + cookieStorage.deleteCookie(cookie) + } + return + } for serverUrl in serverUrls { guard let url = URL(string: serverUrl), let domain = url.host else { continue }