Refs:
What happened?
Picking a Pubky Ring pubky on Pubky Choice can stop halfway and leave a session behind that the app doesn't show. Two paths lead there, both raised in the review of #1329 at 3ff19f5.
- Back before the pick finishes (@jvsena42, and @piotr-iohk's first finding):
adoptRingIdentity runs in the choice screen's viewModelScope. Pressing back cancels it after pubkyService.signIn() has saved PAYKIT_SESSION and SHARED_PUBKY_SOURCE. runSuspendCatching rethrows the CancellationException, so the onFailure cleanup never runs, _publicKey is never set and the pending profile setup flag is never written. The header shows the user signed out; after a relaunch initialize() restores the session and the user is signed in as the abandoned pubky, with no profile setup or contact import.
- Activation fails after the session is saved (@piotr-iohk's second finding):
PaykitSdkService.signIn / signUp call activateBootstrapResult, which saves PAYKIT_SESSION, removes the local PUBKY_SECRET_KEY for an adopted Ring pubky, then calls handle.initialize(). If that throws, the adoption failure handler deletes only SHARED_PUBKY_SOURCE, leaving a saved session with neither a local key nor the Ring reference to sign with.
iOS (synonymdev/bitkit-ios#774) handles both paths differently: its adoption runs in an unstructured Task, and a failed adoption discards the session before clearing the reference.
Expected behavior
An interrupted or failed pick ends in one consistent state: either the pubky is picked and its profile setup can resume after a relaunch, or the user is signed out with no session and no Ring reference left behind.
Steps to Reproduce
- Delete the profile, open Pubky Choice with a Ring pubky listed.
- Tap the Ring pubky, then press back before it finishes loading.
- See the header signed out.
- Relaunch Bitkit: it restores the session and shows the abandoned pubky as signed in, without Create Profile.
Logs / Screenshots / Recordings
From @jvsena42's repro:
Upserted value for key 'SHARED_PUBKY_SOURCE'
Upserted value for key 'PAYKIT_SESSION'
(no "Adopted ring identity" line)
... relaunch ...
Restored paykit session for 'pubkyrc…ib4re3o'
Repro video: https://github.com/user-attachments/assets/a85cbded-aa0e-4565-bfc5-4fa6e84d2dec
Bitkit Version
#1329 at 3ff19f5 (not released)
Device / OS
Android
Reproducibility
Always, when back is pressed after sign-in and before the pick finishes; the activation path needs handle.initialize() to fail.
Additional context
Suggested fix from the review: run the part of adoptRingIdentity that writes to the keychain and signs in under NonCancellable, as signOut() does, or clean up when the pick is cancelled; on activation failure, remove both the session and SHARED_PUBKY_SOURCE. Add regression tests that cancel the pick after sign-in succeeds and that throw from activation after the session is saved.
Also raised by @jvsena42 in the same comment, lower priority:
- When Ring is unavailable at launch, the
MainActivityPubkyAuth alias gets disabled and is only recomputed when publicKey changes, so pubkyauth:// links don't reach Bitkit after Ring comes back until the app restarts.
- The source-loss warning redacts the whole reference, so the log shows
'app.pub…ib4re3o'.
Refs:
What happened?
Picking a Pubky Ring pubky on Pubky Choice can stop halfway and leave a session behind that the app doesn't show. Two paths lead there, both raised in the review of #1329 at 3ff19f5.
adoptRingIdentityruns in the choice screen'sviewModelScope. Pressing back cancels it afterpubkyService.signIn()has savedPAYKIT_SESSIONandSHARED_PUBKY_SOURCE.runSuspendCatchingrethrows theCancellationException, so theonFailurecleanup never runs,_publicKeyis never set and the pending profile setup flag is never written. The header shows the user signed out; after a relaunchinitialize()restores the session and the user is signed in as the abandoned pubky, with no profile setup or contact import.PaykitSdkService.signIn/signUpcallactivateBootstrapResult, which savesPAYKIT_SESSION, removes the localPUBKY_SECRET_KEYfor an adopted Ring pubky, then callshandle.initialize(). If that throws, the adoption failure handler deletes onlySHARED_PUBKY_SOURCE, leaving a saved session with neither a local key nor the Ring reference to sign with.iOS (synonymdev/bitkit-ios#774) handles both paths differently: its adoption runs in an unstructured
Task, and a failed adoption discards the session before clearing the reference.Expected behavior
An interrupted or failed pick ends in one consistent state: either the pubky is picked and its profile setup can resume after a relaunch, or the user is signed out with no session and no Ring reference left behind.
Steps to Reproduce
Logs / Screenshots / Recordings
From @jvsena42's repro:
Repro video: https://github.com/user-attachments/assets/a85cbded-aa0e-4565-bfc5-4fa6e84d2dec
Bitkit Version
#1329 at 3ff19f5 (not released)
Device / OS
Android
Reproducibility
Always, when back is pressed after sign-in and before the pick finishes; the activation path needs
handle.initialize()to fail.Additional context
Suggested fix from the review: run the part of
adoptRingIdentitythat writes to the keychain and signs in underNonCancellable, assignOut()does, or clean up when the pick is cancelled; on activation failure, remove both the session andSHARED_PUBKY_SOURCE. Add regression tests that cancel the pick after sign-in succeeds and that throw from activation after the session is saved.Also raised by @jvsena42 in the same comment, lower priority:
MainActivityPubkyAuthalias gets disabled and is only recomputed whenpublicKeychanges, sopubkyauth://links don't reach Bitkit after Ring comes back until the app restarts.'app.pub…ib4re3o'.