Skip to content

Commit 4fabede

Browse files
committed
fix: normalize pubky session keys
1 parent 51d1e6a commit 4fabede

3 files changed

Lines changed: 44 additions & 24 deletions

File tree

app/src/main/java/to/bitkit/repositories/PubkyRepo.kt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ class PubkyRepo @Inject constructor(
154154
is InitResult.Restored -> {
155155
_publicKey.update { result.publicKey }
156156
_authState.update { PubkyAuthState.Authenticated }
157-
Logger.info("Paykit session restored for '${result.publicKey}'", context = TAG)
157+
Logger.info("Restored paykit session for '${result.publicKey}'", context = TAG)
158158
loadProfile()
159159
loadContacts()
160160
}
@@ -172,7 +172,7 @@ class PubkyRepo @Inject constructor(
172172
): InitResult = withContext(ioDispatcher) {
173173
if (!savedSessionSecret.isNullOrEmpty()) {
174174
runCatching {
175-
val publicKey = pubkyService.importSession(savedSessionSecret)
175+
val publicKey = pubkyService.importSession(savedSessionSecret).ensurePubkyPrefix()
176176
InitResult.Restored(publicKey)
177177
}.getOrElse {
178178
Logger.warn("Failed to restore paykit session, attempting re-sign-in", it, context = TAG)
@@ -199,7 +199,7 @@ class PubkyRepo @Inject constructor(
199199
val newSession = pubkyService.signIn(storedSecretKeyHex)
200200
keychain.upsertString(Keychain.Key.PAYKIT_SESSION.name, newSession)
201201
notifyBackupStateChanged()
202-
val publicKey = pubkyService.importSession(newSession)
202+
val publicKey = pubkyService.importSession(newSession).ensurePubkyPrefix()
203203
Logger.info("Re-signed in and restored session for '$publicKey'", context = TAG)
204204
InitResult.Restored(publicKey)
205205
}.getOrElse {
@@ -232,7 +232,7 @@ class PubkyRepo @Inject constructor(
232232
return runCatching {
233233
withContext(ioDispatcher) {
234234
val sessionSecret = pubkyService.completeAuth()
235-
val pk = pubkyService.importSession(sessionSecret)
235+
val pk = pubkyService.importSession(sessionSecret).ensurePubkyPrefix()
236236

237237
runCatching { keychain.delete(Keychain.Key.PUBKY_SECRET_KEY.name) }
238238
keychain.upsertString(Keychain.Key.PAYKIT_SESSION.name, sessionSecret)
@@ -245,15 +245,15 @@ class PubkyRepo @Inject constructor(
245245
}.onSuccess { pk ->
246246
_publicKey.update { pk }
247247
_authState.update { PubkyAuthState.Authenticated }
248-
Logger.info("Pubky auth completed for '$pk'", context = TAG)
248+
Logger.info("Completed pubky auth for '$pk'", context = TAG)
249249
loadProfile()
250250
}.map { }
251251
}
252252

253253
suspend fun cancelAuthentication() {
254254
runCatching {
255255
withContext(ioDispatcher) { pubkyService.cancelAuth() }
256-
}.onFailure { Logger.warn("Cancel auth failed", it, context = TAG) }
256+
}.onFailure { Logger.warn("Failed to cancel auth", it, context = TAG) }
257257
_authState.update { PubkyAuthState.Idle }
258258
}
259259

@@ -343,7 +343,7 @@ class PubkyRepo @Inject constructor(
343343
val session = runCatching {
344344
pubkyService.signUp(secretKeyHex, homegate.homeserverPubky, homegate.signupCode)
345345
}.getOrElse {
346-
Logger.warn("signUp failed (likely already registered), trying signIn", it, context = TAG)
346+
Logger.warn("Retrying sign in after sign up failed", it, context = TAG)
347347
pubkyService.signIn(secretKeyHex)
348348
}
349349

@@ -357,7 +357,7 @@ class PubkyRepo @Inject constructor(
357357

358358
_publicKey.update { publicKeyZ32 }
359359
_authState.update { PubkyAuthState.Authenticated }
360-
Logger.info("Identity created for '$publicKeyZ32'", context = TAG)
360+
Logger.info("Created identity for '$publicKeyZ32'", context = TAG)
361361
loadProfile()
362362
loadContacts()
363363
}

app/src/main/java/to/bitkit/ui/screens/contacts/AddContactScreen.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ import androidx.compose.runtime.remember
3030
import androidx.compose.runtime.setValue
3131
import androidx.compose.ui.Alignment
3232
import androidx.compose.ui.Modifier
33-
import androidx.compose.ui.platform.testTag
3433
import androidx.compose.ui.draw.clip
3534
import androidx.compose.ui.draw.drawBehind
3635
import androidx.compose.ui.geometry.Offset
@@ -41,6 +40,7 @@ import androidx.compose.ui.graphics.StrokeCap
4140
import androidx.compose.ui.graphics.drawscope.Stroke
4241
import androidx.compose.ui.graphics.drawscope.rotate
4342
import androidx.compose.ui.platform.LocalContext
43+
import androidx.compose.ui.platform.testTag
4444
import androidx.compose.ui.res.painterResource
4545
import androidx.compose.ui.res.stringResource
4646
import androidx.compose.ui.text.input.KeyboardCapitalization

app/src/test/java/to/bitkit/repositories/PubkyRepoTest.kt

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -97,30 +97,30 @@ class PubkyRepoTest : BaseUnitTest() {
9797
@Test
9898
fun `completeAuthentication should save session and update state`() = test {
9999
val testSecret = "session_secret"
100-
val testPk = "completed_pk"
100+
val testPk = VALID_SELF_KEY.removePrefix("pubky")
101101
whenever(pubkyService.completeAuth()).thenReturn(testSecret)
102102
whenever(pubkyService.importSession(testSecret)).thenReturn(testPk)
103103

104104
val ffiProfile = mock<CorePubkyProfile>()
105105
whenever(ffiProfile.name).thenReturn("User")
106-
whenever(pubkyService.getProfile(testPk)).thenReturn(ffiProfile)
106+
whenever(pubkyService.getProfile(VALID_SELF_KEY)).thenReturn(ffiProfile)
107107

108108
val result = sut.completeAuthentication()
109109

110110
assertTrue(result.isSuccess)
111-
assertEquals(testPk, sut.publicKey.value)
111+
assertEquals(VALID_SELF_KEY, sut.publicKey.value)
112112
assertTrue(sut.isAuthenticated.value)
113113
verifyBlocking(keychain) { upsertString(Keychain.Key.PAYKIT_SESSION.name, testSecret) }
114114
}
115115

116116
@Test
117117
fun `completeAuthentication should clear managed secret key`() = test {
118118
val testSecret = "session_secret"
119-
val testPk = "completed_pk"
119+
val testPk = VALID_SELF_KEY.removePrefix("pubky")
120120
whenever(pubkyService.completeAuth()).thenReturn(testSecret)
121121
whenever(pubkyService.importSession(testSecret)).thenReturn(testPk)
122122
val ffiProfile = createFfiProfile(name = "User")
123-
whenever(pubkyService.getProfile(testPk)).thenReturn(ffiProfile)
123+
whenever(pubkyService.getProfile(VALID_SELF_KEY)).thenReturn(ffiProfile)
124124

125125
val result = sut.completeAuthentication()
126126

@@ -131,11 +131,11 @@ class PubkyRepoTest : BaseUnitTest() {
131131
@Test
132132
fun `completeAuthentication should not load contacts automatically`() = test {
133133
val testSecret = "session_secret"
134-
val testPk = "completed_pk"
134+
val testPk = VALID_SELF_KEY.removePrefix("pubky")
135135
whenever(pubkyService.completeAuth()).thenReturn(testSecret)
136136
whenever(pubkyService.importSession(testSecret)).thenReturn(testPk)
137137
val ffiProfile = createFfiProfile(name = "User")
138-
whenever(pubkyService.getProfile(testPk)).thenReturn(ffiProfile)
138+
whenever(pubkyService.getProfile(VALID_SELF_KEY)).thenReturn(ffiProfile)
139139

140140
val result = sut.completeAuthentication()
141141

@@ -420,21 +420,37 @@ class PubkyRepoTest : BaseUnitTest() {
420420
assertNull(result.getOrNull())
421421
}
422422

423+
@Test
424+
fun `initialize should restore saved session with prefixed public key`() = test {
425+
val session = "saved_session"
426+
val unprefixedPublicKey = VALID_SELF_KEY.removePrefix("pubky")
427+
val ffiProfile = createFfiProfile(name = "Restored User")
428+
whenever(keychain.loadString(Keychain.Key.PAYKIT_SESSION.name)).thenReturn(session)
429+
whenever(keychain.loadString(Keychain.Key.PUBKY_SECRET_KEY.name)).thenReturn(null)
430+
whenever(pubkyService.importSession(session)).thenReturn(unprefixedPublicKey)
431+
whenever(pubkyService.getProfile(VALID_SELF_KEY)).thenReturn(ffiProfile)
432+
433+
sut.initialize()
434+
435+
assertEquals(VALID_SELF_KEY, sut.publicKey.value)
436+
assertTrue(sut.isAuthenticated.value)
437+
}
438+
423439
@Test
424440
fun `initialize should restore session from local secret key when saved session is missing`() = test {
425441
val secretKey = "local_secret"
426442
val session = "new_session"
427-
val publicKey = VALID_SELF_KEY
443+
val publicKey = VALID_SELF_KEY.removePrefix("pubky")
428444
val ffiProfile = createFfiProfile(name = "Recovered User")
429445
whenever(keychain.loadString(Keychain.Key.PAYKIT_SESSION.name)).thenReturn(null)
430446
whenever(keychain.loadString(Keychain.Key.PUBKY_SECRET_KEY.name)).thenReturn(secretKey)
431447
whenever(pubkyService.signIn(secretKey)).thenReturn(session)
432448
whenever(pubkyService.importSession(session)).thenReturn(publicKey)
433-
whenever(pubkyService.getProfile(publicKey)).thenReturn(ffiProfile)
449+
whenever(pubkyService.getProfile(VALID_SELF_KEY)).thenReturn(ffiProfile)
434450

435451
sut.initialize()
436452

437-
assertEquals(publicKey, sut.publicKey.value)
453+
assertEquals(VALID_SELF_KEY, sut.publicKey.value)
438454
assertTrue(sut.isAuthenticated.value)
439455
verifyBlocking(keychain) { upsertString(Keychain.Key.PAYKIT_SESSION.name, session) }
440456
}
@@ -559,14 +575,14 @@ class PubkyRepoTest : BaseUnitTest() {
559575
whenever(keychain.loadString(Keychain.Key.PAYKIT_SESSION.name)).thenReturn(newSecret)
560576
whenever(pubkyService.sessionList(newSecret, Env.contactsBasePath)).thenReturn(emptyList())
561577
val staleProfile = createFfiProfile(name = "Stale Old")
562-
whenever(pubkyService.getProfile(oldPublicKey)).thenAnswer {
578+
whenever(pubkyService.getProfile(oldPublicKey.ensurePubkyPrefixForTest())).thenAnswer {
563579
runBlocking { sut.completeAuthentication() }
564580
staleProfile
565581
}
566582

567583
sut.loadProfile()
568584

569-
assertEquals(newPublicKey, sut.publicKey.value)
585+
assertEquals(newPublicKey.ensurePubkyPrefixForTest(), sut.publicKey.value)
570586
assertEquals("Initial Old", sut.profile.value?.name)
571587
}
572588

@@ -599,7 +615,7 @@ class PubkyRepoTest : BaseUnitTest() {
599615
whenever(pubkyService.completeAuth()).thenReturn(newSecret)
600616
whenever(pubkyService.importSession(newSecret)).thenReturn(newPublicKey)
601617
val newProfile = createFfiProfile(name = "New User")
602-
whenever(pubkyService.getProfile(newPublicKey)).thenReturn(newProfile)
618+
whenever(pubkyService.getProfile(newPublicKey.ensurePubkyPrefixForTest())).thenReturn(newProfile)
603619
whenever(keychain.loadString(Keychain.Key.PAYKIT_SESSION.name)).thenReturn(oldSecret)
604620
whenever(pubkyService.sessionList(oldSecret, Env.contactsBasePath)).thenReturn(listOf(staleContactPath))
605621
whenever(pubkyService.fetchFileString(staleContactUri)).thenAnswer {
@@ -610,7 +626,7 @@ class PubkyRepoTest : BaseUnitTest() {
610626
sut.loadContacts()
611627

612628
val contacts = sut.contacts.value
613-
assertEquals(newPublicKey, sut.publicKey.value)
629+
assertEquals(newPublicKey.ensurePubkyPrefixForTest(), sut.publicKey.value)
614630
assertEquals(1, contacts.size)
615631
assertEquals(existingContact.publicKey, contacts.first().publicKey)
616632
assertEquals(existingContact.name, contacts.first().name)
@@ -855,10 +871,11 @@ class PubkyRepoTest : BaseUnitTest() {
855871
secret: String = "test_secret",
856872
profileName: String = "Test",
857873
) {
874+
val prefixedPublicKey = publicKey.ensurePubkyPrefixForTest()
858875
whenever { pubkyService.completeAuth() }.thenReturn(secret)
859876
whenever { pubkyService.importSession(secret) }.thenReturn(publicKey)
860877
val ffiProfile = createFfiProfile(name = profileName)
861-
whenever { pubkyService.getProfile(publicKey) }.thenReturn(ffiProfile)
878+
whenever { pubkyService.getProfile(prefixedPublicKey) }.thenReturn(ffiProfile)
862879
whenever(keychain.loadString(Keychain.Key.PAYKIT_SESSION.name)).thenReturn(secret)
863880
whenever { pubkyService.sessionList(secret, Env.contactsBasePath) }.thenReturn(emptyList())
864881

@@ -874,3 +891,6 @@ class PubkyRepoTest : BaseUnitTest() {
874891
return ffiProfile
875892
}
876893
}
894+
895+
private fun String.ensurePubkyPrefixForTest(): String =
896+
if (startsWith("pubky")) this else "pubky$this"

0 commit comments

Comments
 (0)