Skip to content

fix(core): report expired sessions without logging the passkey out - #356

Draft
mariano-aguero wants to merge 15 commits into
mainfrom
mariano/core-testing-gates-followups
Draft

mariano-aguero wants to merge 15 commits into
mainfrom
mariano/core-testing-gates-followups

Conversation

@mariano-aguero

@mariano-aguero mariano-aguero commented Sep 23, 2026

Copy link
Copy Markdown
Contributor

What

When a session expires while the page is open, the provider now tells the dApp: it drops the signer, emits accountsChanged with an empty list and disconnect, and answers later requests as it would for a first visit. The passkey stays logged in and the transport stays up, so reconnecting takes one approval. Signing requests made without a session also clean up their temporary signer when they fail.

Why

A signer restored from an earlier visit kept answering after its session had expired. The dApp kept showing the account, and the first signing request came back with 4100, which the provider read as a dead session and tore down completely: passkey logged out, iframe dropped. The provider now treats a signer with no stored account as not connected and reports it once.

A 4100 now disconnects only the session the failing request went through. A late error from a signer that was already dropped cannot log out a session a parallel connect just set up, and a reconnect approved while a read dropped the signer puts that signer back and emits connect.

The ephemeral signer used for wallet_sendCalls, wallet_sign, the permission methods and wallet_addFunds left session keys (CrossPlatform) or a persisted account (AppSpecific) behind when the request was rejected. Cleanup now runs in a finally, and AppSpecificSigner.cleanup clears its own state even when the UI handler's cleanup throws. If a connect finished while the temporary signer was open, its cleanup is skipped, because the stored account, signer type and keys now belong to the new session.

Backend refusals from #321 are kept: a 4100 the backend marks as a refusal still never disconnects, so the teardown now needs all three: a 4100, from the signer this request used, that is not a backend refusal. Two new tests send a 403 through the real AppSpecific signer, on a live session and on an expired one, and both fail if the refusal check is removed.

The provider conformance suite now runs in both authentication modes, and the comment on the chain lists says the readonly guard is shallow.

Closes N/A. Part of ENGR-1282.

How to test

bun install
bunx nx run-many -t typecheck lint test --projects=@jaw.id/core
bunx nx build @jaw.id/core

By hand, in the playground in AppSpecific mode: connect, then shorten the session (or move the clock past the auth TTL) and call eth_accounts. The dApp should receive accountsChanged with [] and one disconnect, and a reconnect should ask for the passkey once without a fresh login. Rejecting a wallet_sendCalls made while disconnected should leave no stored account behind.

Checklist

  • The title is a conventional commit and reads as the subject it becomes on squash. nx release takes the version bump from its type
  • bunx prettier --check . and bunx nx affected -t lint test typecheck build api-check pass, which is what CI runs
  • @jaw.id/core's public API is unchanged, or bunx nx api-update @jaw.id/core ran and the report diff is part of this PR
  • Docs updated, if this changes behaviour somebody integrates against

@vercel

vercel Bot commented Sep 23, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
demo Ready Ready Preview Sep 23, 2026 5:01pm UTC
jaw-docs Ready Ready Preview Sep 23, 2026 5:01pm UTC
keys-jaw-id Ready Ready Preview Sep 23, 2026 5:01pm UTC
playground Ready Ready Preview Sep 23, 2026 5:01pm UTC

Request Review

@linear

linear Bot commented Sep 23, 2026

Copy link
Copy Markdown

ENGR-1282

@justalab-agents justalab-agents Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Findings outside the diff

These sit on lines this PR did not change, but this PR is what makes them wrong. They cannot be posted as inline comments.

Reviewer callouts (1)

Non-blocking observations. Nothing here needs to change before merge.

  • .github:1 — "The ubuntu-latest label will migrate to Ubuntu 26 beginning October 19, 2026. For more information, see actions/runner-images#14748"

This branch was successfully deployed

4 active deployments
Preview – jaw-docs 38d16648 Deployed Sep 23, 2026 by vercel[bot]
Preview – demo 38d16648 Deployed Sep 23, 2026 by vercel[bot]
Preview – playground 38d16648 Deployed Sep 23, 2026 by vercel[bot]
Preview – keys-jaw-id 38d16648 Deployed Sep 23, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

testing gates Work from the testing rigor plan (ENGR-1230)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant