Repository navigation
fix(auth): never sign as a conflicting source when the pin is missing- #3094 - #1069
Open
aristidesstaffieri wants to merge 14 commits into
Open
aristidesstaffieri wants to merge 14 commits into
aristidesstaffieri wants to merge 14 commits into
Conversation
Ports stellar/freighter#3057. Settings gets an Accounts & recovery card that lists accounts grouped by the recovery phrase or secret key behind them. Edit account renames an account, opens it on stellar.expert, and reveals its secret key or recovery phrase after a password or biometric check. revealAccountSecretKey and revealRecoveryPhrase in ducks/auth check the session is still authenticated before and after decrypting, and a phrase is only returned for the source the account belongs to.
Accounts & recovery waits for the secret sources before listing accounts, so rows no longer appear ungrouped and then regroup. Rename on Edit account opens the floating bottom sheet; Home keeps the centered modal.
Ports stellar/freighter#3059. The switcher groups wallets under the recovery phrase or secret key behind them, with collapsible headers that stay collapsed until the app restarts. A wallet with one source stays a flat list. The header keeps only the address and a copy button, since rename and the explorer link are on Edit account and QR is on the scan screen. Rows drop the "Imported" tag, and the unused rename modal is removed.
Port of stellar/freighter#3082. Add wallet now opens an "Add account" chooser with Create new wallet and Import Secret Key. Create new wallet lists the wallet's recovery phrases, so an account can be added under an existing phrase or under a newly generated one. Each new account is named in a sheet prefilled with the next free "Account N". A new phrase is held in auth module state, never in store state or navigation params, until the user backs it up and confirms with their password. Each generation gets an id that the screens pass along, and saving refuses a phrase that a later generation replaced. The held phrase is cleared once saved, when the backup screen closes, and on lock, logout or wipe. Import Recovery Phrase and Connect Hardware Wallet are left out. Mobile has no hardware support, and importing a phrase still replaces the whole wallet.
Port of stellar/freighter#3086. Sign-up, import, sign-in, add, rename, switch and the wipe's storage clear now run one at a time behind a promise-chain lock. A lazily loaded private key is written to a fresh read of the temporary store under the lock. A switch drops its account if a soft-lock lands mid-switch. Funded-account lookups during a recovery-phrase import time out after 5s.
…he wallet Port of stellar/freighter#3084. Add wallet now offers Import Recovery Phrase, which adds a typed 12- or 24-word phrase as a new source with index 0 and any funded indices up to 49 on mainnet. Index 0 becomes active and nothing already in the wallet changes. A held phrase, an address already in the wallet or a wrong password is refused before anything is written. The funded-account lookups run before the account-mutation lock is taken, and the checks and writes run under it. If no account gets stored, the source and its indices are removed again.
Port of stellar/freighter#3091. Import Wallet takes one input and works out whether it holds a 12 or 24-word recovery phrase or a Stellar secret key. The field keeps a secret key's case. A secret key goes through the new importWalletFromSecretKey store action, which stores one imported account with a secret-key source and no phrase. It checks the key before wiping anything and runs behind the account-mutation lock. If storing fails after the wipe, it wipes again so the user can retry. Set Password names the secret key in its acknowledgement, and both the Set Password and Biometrics steps can finish a secret-key import. Security hides "Show recovery phrase" for a wallet that holds only secret keys. The import is tracked as account_recovery.completed or account_recovery.failed with recovery_method "secret_key".
Port of the minimum importing time from stellar/freighter#3091 (835b23a8). runWithWalletSetupOverlay waits at least SETTING_UP_MIN_MS (500ms) before turning ready or fading out, so a secret-key import doesn't flash "Importing your wallet...". The Biometrics screen tests run on fake timers so the wait stays inside each test.
storeKeys removes a key it just wrote if adding it to the index fails, then rethrows the index error. clearAllData also removes every stellarwallet_* keychain entry after the index-based removal, so an entry the index doesn't list can't survive a wipe.
importWalletFromSecretKey and importSecretKey send a fixed reason_code to analytics instead of the scrubbed error text, which can carry the key in a form scrubStrKeys misses (key_S…). Adds the missing trackAccountScreenImportAccountFail to the analytics mock in jest.setup.js.
…lback storeAccount waits for every write it started before it throws, using a new settleAll helper, so nothing lands after a rollback. Failed sign-up, phrase import and key import share rollBackWalletSetup, which also removes BIOMETRIC_PASSWORD. The Biometrics screen saves the password again before each Enable attempt so a retry still works.
signUp, importWallet and importWalletFromSecretKey reset auto-lock themselves, just before the pre-store wipe, instead of in their store actions before validation. An invalid secret key now leaves the timer, the background timestamp and the LOCKED marker as they were. The reset still runs under the account-mutation lock and before the new hash key.
useHasRecoveryPhrase falls back to true when reading the secret sources fails, as its doc says, so a stale false from an earlier key-only read can't hide "Show recovery phrase" after a phrase is added.
aristidesstaffieri
marked this pull request as ready for review
November 13, 2026 23:06
Contributor
|
iOS Simulator preview build is ready: https://github.com/stellar/freighter-mobile/releases/tag/untagged-4d0daa5cc32d59c1b1e4 |
Contributor
|
Android Emulator preview APK is ready: https://github.com/stellar/freighter-mobile/releases/tag/untagged-9f1154ad0f7fa1593584 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
When the pinned source isn't held,
getSigningIdentitynow picks the fallback signer by link status: a held source the server listed aslinked, then one it hasn't answered for yet, with phrases first in each tier. It never signs with a source the server reported as aconflict, and if only those are held it returnsnullso requests go out anonymously. It still never writes the pin. New tests cover each tier, the all-conflict case, and a held pin that has no status.Why
The fallback signed with the first held source. If the server had marked that source as another user's, every request would resolve to that user and the analytics reconcile from stellar/freighter-private#69 would cache their canonical id. The link status map from stellar/freighter-private#68 already records which sources are in this wallet's cluster, so the fallback can rank by it without another request. This covers the conflict half of the re-pin that stellar/freighter-private#66 asked for. Today the pinned source goes missing only when it fails to derive at unlock, since recovery wipes the wallet and nothing removes accounts.
Screenshots / video
N/A, background only.
Checklist