Skip to content

The guest prompt's yes said nothing, and no kept the guest session (GRYT-1225) - #587

Merged
sivert-io merged 1 commit into
mainfrom
claude/GRYT-1225-claim-edge-cases
Sep 15, 2026
Merged

sivert-io merged 1 commit into
mainfrom
claude/GRYT-1225-claim-edge-cases

Conversation

@sivert-io

Copy link
Copy Markdown
Member

GRYT-1225. This needs Gryt-chat/server#193 merged and released first, and that one is review-required. Against a server without it, yes still says nothing and a merge can't happen.

Nothing here touches src/packages/common/src/auth/.

Yes

After yes, the client now says what the server did, going by identityClaim on server:joined:

Server says Toast
carried Moved the guest to your account.
merged Moved your guest messages to your account. Your account keeps its name, picture and roles.
no_prior_membership This server doesn't have a guest from this device, so nothing moved.
failed The server couldn't move the guest to your account.

Only the join within 20 seconds of the click gets one, since every later join reports the stored yes again. A server without the field gets no toast.

chat:merge_user rewrites the sender and reactions on messages the client holds, in the channel, the message cache and an open thread. Somebody who reacted as both the guest and the account counts once.

No

No now drops this device's session for that server and rejoins, so from then on it's the account there. The guest's keys and visit history stay on the device, since "I've used this server before" needs them later. On the server, the guest stays as it was. If the account isn't allowed in, the usual refusal shows, and with the server change the socket stops being the guest too.

Ask me later is unchanged: nothing is stored, nothing reconnects, and it asks again next launch.

Two bugs found while checking it

  • useServerState memoized the access token on tokenRevision, and only token:refreshed bumped it. After either answer, the reports list, admin actions, the sidebar editor and report user kept sending the guest's old token. After a merge that token belongs to nobody. The membership_required it got back wiped the session the join had just stored, and the confirmation never showed. A join bumps tokenRevision now too.
  • A second tab still on the guest gets token:revoked with identity_merged. Its usual retry reads the refresh token from storage. When the other tab's change to storage hadn't reached it yet, that sent the guest's revoked token, got refresh_token_invalid and gave up. For identity_merged it now drops the refresh token first and rejoins.

scripts/check-identity-claim.mjs (yarn test:identity-claim, added to CI) checks the toasts, the 20 second window and mergeSender.

Checked

I drove this branch in headless Chromium against a throwaway server running the server branch. I read the DOM, the socket frames and the server's database. A real Keycloak sign-in needs credentials I can't type, so I faked the account. These were local, uncommitted changes, and they're reverted:

  • getValidIdentityToken, useAccount and useUserId answered for a fake account when localStorage had a gryt_fake_account key.
  • A small local identity service signed the account's certificate, and the server trusted it through GRYT_TRUSTED_CERT_ISSUERS.
  • The account's store was seeded with the guest's server list and a nickname.

65 checks passed across yes with a merge, yes with a move, ask me later, no, no when refused, a second tab, nothing to move, and a failed merge. The toasts above were read off the page. After either answer, every token the client sent belonged to the account. Taking out the tokenRevision bump brought back "You are no longer a member of this server" and a reports:list with the guest's token. Taking out the chat:merge_user handler left the phone showing the guest's name on the moved message.

After no, a relaunch restored the account's session, and nothing asked again.

yarn lint, yarn build, every test:* step in ci.yml and yarn tsc -p e2e pass. yarn e2e passed 14 of 14 against ghcr.io/gryt-chat/server:latest. Against the server branch 13 passed, and the phone settings test skipped because it needs a server of its own.

🤖 Generated with Claude Code

…RYT-1225)

Yes now shows what the server did, from identityClaim on server:joined, and
only for the join right after the click. chat:merge_user rewrites the sender
and reactions on messages the client holds, in the channel and an open thread.

No drops this device's session for that server and rejoins as the account. The
guest's keys and visit history stay on the device, and the guest stays as it
was on the server. Ask me later is unchanged.

A join bumps tokenRevision now, as a refresh does. Hooks reading the access
token from useServerState kept the guest's old one after either answer. After
a merge, the membership_required that token got back wiped the new session.

A second tab that gets token:revoked with identity_merged drops its refresh
token too and rejoins. That token names a membership that's gone.

Co-Authored-By: Claude Opus 5 <[email protected]>
@sivert-io
sivert-io merged commit 6e23bf8 into main Sep 15, 2026
5 checks passed
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.

1 participant