Skip to content

fix(wdk): keep signed-in wallets on sign-up and allow reusing a stored passkey - #1058

Merged
taylanpince merged 3 commits into
masterfrom
fix/signup-keep-signed-in-wallets
Nov 2, 2026
Merged

taylanpince merged 3 commits into
masterfrom
fix/signup-keep-signed-in-wallets

Conversation

@tolgahan-arikan

@tolgahan-arikan tolgahan-arikan commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Two signUp fixes in @0xsequence/wallet-wdk, needed to create a second wallet for a login that already has one signed in on this device. wallet-webapp-v3 uses this to move users off wallets set up by an older SDK (0xsequence/wallet-webapp-v3#393) and carries both changes as a pnpm patch on 3.1.3 until they're released.

Changes

Keep signed-in wallets when signing up again with the same login (d345317e0)

  • signUp wrote a placeholder entry for every existing wallet of the login. It then deleted them when the wallet selector returned create-new, or kept only those with a device on abort-signup.
  • So a wallet already signed in on this device lost its session.
  • Now placeholders are only added, and cleaned up, for wallets without a session. Stored entries are matched regardless of address casing.

Sign up with a stored passkey (28210bcd7)

  • Passkey sign-up always created a new WebAuthn credential, so a second wallet for a passkey user got a separate login.
  • New credentialId argument on passkey sign-up: the signer is built from the stored credential, with the credential id in its metadata so other devices can load it from the witness. The stored credential record then points at the new wallet.

Embed a stored passkey's metadata when signing with it (ecb6c4c1c)

  • A passkey's image hash includes its public key's metadata (the credential id) when the key has any. Signatures only carry that metadata when the signer's embedMetadata is set, and whatever decodes a signature rebuilds the public key from what it carries.

  • Signers built from a stored credential hardcoded embedMetadata: false. That covers both sign-up with credentialId (new above) and login with credentialId (already on master). So for a public key with metadata, the signature verified but proved a different image hash. The new wallet's witness was indexed under that hash, and loading the passkey for the wallet failed with "Witness for wallet not found".

  • The flag is now derived from the stored key: embedMetadata: credential.publicKey.metadata !== undefined. That's the same as signing in by passkey discovery (loadFromWitness), which already takes it from the witness.

  • No change for existing passkeys. Passkey.create only adds metadata when asked to, and the WDK's sign-up never asks, so passkeys created through the WDK have none. For them the flag stays false, and they sign exactly as before. I checked this with the real signer and encoder for both kinds of key:

    Stored key Before After
    No metadata proves the configured image hash same encoding, same image hash
    Embedded metadata proves a different image hash proves the configured image hash

    A stored key's metadata always matches the wallet's configuration: the credential store saves the signer's own public key, and a passkey's witness carries that same key.

All three changes have patch changesets.

Testing

  • test/wallets.test.ts (43 tests, including a regression for the kept session) and the new test/passkey-reuse.test.ts (5 tests) pass. The embedded-metadata case fails without the fix, with "Witness for wallet not found".
  • pnpm typecheck, pnpm lint and Prettier pass for the package.
  • The suites that need the Anvil fork (recovery, sessions, messages) weren't run locally; CI's tests.yml runs them.
  • Used end to end from wallet-webapp-v3 (through the pnpm patch) with passkey, Google and email logins on dev.

…login

signUp wrote a placeholder entry for every existing wallet of the login and
deleted them when the wallet selector returned create-new (or kept only
those with a device on abort-signup). A wallet already signed in on this
device lost its session. Only add and clean up placeholders for wallets
without a session, matching stored entries regardless of address casing.
Passkey sign-up always created a new WebAuthn credential, so a second wallet
for a passkey user got a separate login. With credentialId the signer is built
from the stored credential (with the credential id in its metadata, so other
devices can load it from the witness) and the stored record now points at the
new wallet.
@tolgahan-arikan
tolgahan-arikan requested a review from a team November 2, 2026 12:15
@tolgahan-arikan
tolgahan-arikan requested a review from a team as a code owner November 2, 2026 12:15
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-02T12:17:29.709532Z 28210bc PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

A passkey's image hash includes its public key's metadata. Signers built
from a stored credential always set embedMetadata to false, so for a
public key with metadata the signature proved a different image hash and
its witness was indexed under it. Derive the flag from the public key, for
sign-up with a stored passkey and for login with a credential id.
@taylanpince
taylanpince merged commit bcb6ef8 into master Nov 2, 2026
10 of 11 checks passed
@taylanpince
taylanpince deleted the fix/signup-keep-signed-in-wallets branch November 2, 2026 13:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants