From 5e095bd96e425bc5997cd71ce21bc4a2cb873724 Mon Sep 17 00:00:00 2001 From: mertalev <101130780+mertalev@users.noreply.github.com> Date: Tue, 10 Mar 2026 13:36:17 -0500 Subject: [PATCH] use constants --- .../alextran/immich/core/HttpClientManager.kt | 37 ++++++++++++------- mobile/ios/Runner/Core/NetworkApiImpl.swift | 20 +++++----- .../ios/Runner/Core/URLSessionManager.swift | 31 +++++++++++++--- 3 files changed, 59 insertions(+), 29 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 feba043e02..180ae4735d 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 @@ -39,6 +39,17 @@ private const val PREFS_CERT_ALIAS = "immich.client_cert" private const val PREFS_HEADERS = "immich.request_headers" private const val PREFS_SERVER_URLS = "immich.server_urls" private const val PREFS_COOKIES = "immich.cookies" +private const val COOKIE_EXPIRY_DAYS = 400L + +private enum class AuthCookie(val cookieName: String, val httpOnly: Boolean) { + ACCESS_TOKEN("immich_access_token", httpOnly = true), + IS_AUTHENTICATED("immich_is_authenticated", httpOnly = false), + AUTH_TYPE("immich_auth_type", httpOnly = true); + + companion object { + val names = entries.map { it.cookieName }.toSet() + } +} /** * Manages a shared OkHttpClient with SSL configuration support. @@ -189,18 +200,19 @@ object HttpClientManager { if (token != null) { val url = serverUrls.firstNotNullOfOrNull { it.toHttpUrlOrNull() } ?: return - val expiry = System.currentTimeMillis() + 400L * 24 * 60 * 60 * 1000 - fun cookie(name: String, value: String, httpOnly: Boolean) = - Cookie.Builder().name(name).value(value).domain(url.host).path("/").expiresAt(expiry) + val expiry = System.currentTimeMillis() + COOKIE_EXPIRY_DAYS * 24 * 60 * 60 * 1000 + val values = mapOf( + AuthCookie.ACCESS_TOKEN to token, + AuthCookie.IS_AUTHENTICATED to "true", + AuthCookie.AUTH_TYPE to "password", + ) + cookieJar.saveFromResponse(url, values.map { (cookie, value) -> + Cookie.Builder().name(cookie.cookieName).value(value).domain(url.host).path("/").expiresAt(expiry) .apply { if (url.isHttps) secure() - if (httpOnly) httpOnly() + if (cookie.httpOnly) httpOnly() }.build() - cookieJar.saveFromResponse(url, listOf( - cookie("immich_access_token", token, httpOnly = true), - cookie("immich_is_authenticated", "true", httpOnly = false), - cookie("immich_auth_type", "password", httpOnly = true), - )) + }) } } } @@ -300,9 +312,6 @@ object HttpClientManager { private var serverUrls = listOf() private var prefs: SharedPreferences? = null - companion object { - val AUTH_COOKIE_NAMES = setOf("immich_access_token", "immich_is_authenticated", "immich_auth_type") - } fun init(prefs: SharedPreferences) { this.prefs = prefs @@ -344,11 +353,11 @@ object HttpClientManager { 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 } + .filter { it.name in AuthCookie.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 } + return store.removeAll { it.name in AuthCookie.names && it.domain in serverHosts } } var changed = false diff --git a/mobile/ios/Runner/Core/NetworkApiImpl.swift b/mobile/ios/Runner/Core/NetworkApiImpl.swift index 310ff28a44..3c4be8e718 100644 --- a/mobile/ios/Runner/Core/NetworkApiImpl.swift +++ b/mobile/ios/Runner/Core/NetworkApiImpl.swift @@ -62,27 +62,27 @@ class NetworkApiImpl: NetworkApi { URLSessionManager.setServerUrls(serverUrls) if let token = token { - let expiry = Date().addingTimeInterval(400 * 24 * 60 * 60) + let expiry = Date().addingTimeInterval(COOKIE_EXPIRY_DAYS * 24 * 60 * 60) for serverUrl in serverUrls { guard let url = URL(string: serverUrl), let domain = url.host else { continue } let isSecure = serverUrl.hasPrefix("https") - let cookies: [(String, String, Bool)] = [ - ("immich_access_token", token, true), - ("immich_is_authenticated", "true", false), - ("immich_auth_type", "password", true), + let values: [AuthCookie: String] = [ + .accessToken: token, + .isAuthenticated: "true", + .authType: "password", ] - for (name, value, httpOnly) in cookies { + for (cookie, value) in values { var properties: [HTTPCookiePropertyKey: Any] = [ - .name: name, + .name: cookie.name, .value: value, .domain: domain, .path: "/", .expires: expiry, ] if isSecure { properties[.secure] = "TRUE" } - if httpOnly { properties[.init("HttpOnly")] = "TRUE" } - if let cookie = HTTPCookie(properties: properties) { - URLSessionManager.cookieStorage.setCookie(cookie) + if cookie.httpOnly { properties[.init("HttpOnly")] = "TRUE" } + if let httpCookie = HTTPCookie(properties: properties) { + URLSessionManager.cookieStorage.setCookie(httpCookie) } } } diff --git a/mobile/ios/Runner/Core/URLSessionManager.swift b/mobile/ios/Runner/Core/URLSessionManager.swift index 31b60bcb4e..10415a0738 100644 --- a/mobile/ios/Runner/Core/URLSessionManager.swift +++ b/mobile/ios/Runner/Core/URLSessionManager.swift @@ -5,6 +5,28 @@ let CLIENT_CERT_LABEL = "app.alextran.immich.client_identity" let HEADERS_KEY = "immich.request_headers" let SERVER_URLS_KEY = "immich.server_urls" let APP_GROUP = "group.app.immich.share" +let COOKIE_EXPIRY_DAYS: TimeInterval = 400 + +enum AuthCookie: CaseIterable { + case accessToken, isAuthenticated, authType + + var name: String { + switch self { + case .accessToken: return "immich_access_token" + case .isAuthenticated: return "immich_is_authenticated" + case .authType: return "immich_auth_type" + } + } + + var httpOnly: Bool { + switch self { + case .accessToken, .authType: return true + case .isAuthenticated: return false + } + } + + static let names: Set = Set(allCases.map(\.name)) +} extension UserDefaults { static let group = UserDefaults(suiteName: APP_GROUP)! @@ -48,9 +70,9 @@ class URLSessionManager: NSObject { Self.serverUrls = UserDefaults.group.stringArray(forKey: SERVER_URLS_KEY) ?? [] NotificationCenter.default.addObserver( Self.self, - selector: #selector(cookiesDidChange), + selector: #selector(Self.cookiesDidChange), name: NSHTTPCookieManagerCookiesChangedNotification, - object: cookieStorage + object: Self.cookieStorage ) } @@ -71,13 +93,12 @@ class URLSessionManager: NSObject { } 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) + AuthCookie.names.contains($0.name) && serverHosts.contains($0.domain) } var sourceCookies: [String: HTTPCookie] = [:] @@ -111,7 +132,7 @@ class URLSessionManager: NSObject { .value: source.value, .domain: domain, .path: "/", - .expires: source.expiresDate ?? Date().addingTimeInterval(400 * 24 * 60 * 60), + .expires: source.expiresDate ?? Date().addingTimeInterval(COOKIE_EXPIRY_DAYS * 24 * 60 * 60), ] if isSecure { properties[.secure] = "TRUE" } if source.isHTTPOnly { properties[.init("HttpOnly")] = "TRUE" }