Fix multi-profile token leakage fallback bug (#337)

This commit modifies `AuthManager.getToken()` to strictly return the chosen profile's token or `null` if the profile has no token, removing the previous logic that incorrectly fell back to the global token (`KEY_TOKEN`).

Why this was necessary:
Before this change, if a user switched to a profile that lacked a saved token (Profile B), the system would fall back to the global token, effectively leaking Profile A's credentials to Profile B's configured host. This was an unintended and critical cross-profile authentication leak.

Impact:
This ensures strong isolation between user profiles. If a profile doesn't have its own credentials, the system will correctly register it as unauthenticated rather than trying to reuse potentially sensitive global tokens.
Unit tests have also been updated to ensure the fallback logic is tested as strictly returning `null`.

Fixes #258
This commit is contained in:
M57 authored and GitHub committed 2026-06-23 12:40:33 +03:00
1 parent 96b9a14e28
commit 5295b7762e
2 files changed
+10 -11

No files matched your search

@@ -139,11 +139,11 @@ class AuthManagerTest {
}
@Test
fun testGetToken_fallsBackToGlobalWhenProfileTokenMissing() {
fun testGetToken_returnsNullWhenProfileTokenMissing() {
every { mockPrefs.getString("selected_profile_id", null) } returns "prof-1"
every { mockPrefs.getString("token_prof-1", null) } returns null // no profile token
every { mockPrefs.getString("auth_token", null) } returns "global-token"
assertEquals("global-token", AuthManager.getToken())
assertNull(AuthManager.getToken())
}
@Test
@@ -293,18 +293,18 @@ class AuthManagerTest {
}
@Test
fun testGetToken_fallsBackToGlobalWhenSelectedProfileLacksToken() {
// Profile A is selected but has no token → falls back to global
fun testGetToken_returnsNullWhenSelectedProfileLacksToken() {
// Profile A is selected but has no token → should return null, not global
every { mockPrefs.getString("selected_profile_id", null) } returns "prof-a"
every { mockPrefs.getString("token_prof-a", null) } returns null
every { mockPrefs.getString("auth_token", null) } returns "shared-global-token"
assertEquals("shared-global-token", AuthManager.getToken())
assertNull(AuthManager.getToken())
// Switch to Profile B, which also has no token → same global fallback
// Switch to Profile B, which also has no token → should return null
every { mockPrefs.getString("selected_profile_id", null) } returns "prof-b"
every { mockPrefs.getString("token_prof-b", null) } returns null
assertEquals("shared-global-token", AuthManager.getToken())
assertNull(AuthManager.getToken())
}
@Test
@@ -314,11 +314,11 @@ class AuthManagerTest {
every { mockPrefs.getString("token_prof-a", null) } returns "token-a"
assertEquals("token-a", AuthManager.getToken())
// Profile B has no token, but global exists
// Profile B has no token, but global exists → should return null
every { mockPrefs.getString("selected_profile_id", null) } returns "prof-b"
every { mockPrefs.getString("token_prof-b", null) } returns null
every { mockPrefs.getString("auth_token", null) } returns "fallback"
assertEquals("fallback", AuthManager.getToken())
assertNull(AuthManager.getToken())
// Profile C has its own token too
every { mockPrefs.getString("selected_profile_id", null) } returns "prof-c"