Repository navigation
Add Switchify account sign-in with an emailed code - #990
Merged
Merged
Conversation
Sign in with the same Supabase account as the Android app. Rust calls the auth REST API (request code, verify, refresh, sign out) so the webview CSP stays closed. The refresh token is kept in the OS keychain under com.enaboapps.switchify.pc.account; access tokens stay in memory and no token, code or email is logged or sent to the webview beyond the address shown to the user. A rejected refresh token signs out; outages do not. Settings gains an Account tab to request a code, sign in, sign out and delete the account through the shared delete_user_account RPC, with a confirmation step. Release builds now require SWITCHIFY_SUPABASE_URL and SWITCHIFY_SUPABASE_PUBLISHABLE_KEY; builds without them show accounts as unavailable. Refs #984 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Address review on account sign-in: - Keep a server-rotated refresh token in memory even if the keychain write fails, so the revoked token is never reused. - Clear the in-memory session before deleting from the keychain, so a keychain failure never leaves a deleted or signed-out account showing. - Retry a keychain that could not be read at startup (e.g. locked at login) and tell the user, instead of silently appearing signed out. - Account tab: controls stay enabled while busy so focus is never dropped, focus moves to the next control after each step, opening the tab no longer steals focus, and repeated errors are announced again. - Release jobs require an https Supabase URL. - Tests cover rotated-token save failure, locked and corrupt keychain entries, keychain delete failure and the unchanged store on a mismatched refresh. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Address re-review on account sign-in: - Account commands emit the current account state even when they fail, so a session rejected mid-action never leaves a stale signed-in view. - Only user actions re-read an unreadable keychain; background calls do not, so they cannot trigger keychain prompts. - Undecodable or ambiguous keychain entries are removed and treated as signed out instead of blocking sign-in forever. - Buttons marked aria-disabled look disabled. - Correct the comment on a failed keychain delete, and make the local stack test read the code after "enter the code:" (the magic-link token can contain digit runs). - Tests cover background no-retry, sign-out with a failing keychain delete, and repeat presses while a request is pending. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Address final review on account sign-in: - A screen change pushed by the backend (e.g. a session rejected during delete) clears any pending delete confirmation and code, so the next sign-in never opens one click from deletion. - If that change removes the focused control, focus moves to the new screen's first control or the keychain notice; focus elsewhere is kept. - Post-action events report state without re-reading the keychain, so a locked keychain prompts at most once per action. - aria-disabled buttons show as GrayText in forced colours. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
enaboapps
marked this pull request as ready for review
October 5, 2026 12:23
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.
Closes #984
Part of #988.
What
src-tauri/src/account.rs: sign-in with an emailed 6-digit code against the shared Supabase project, using the same accounts as Switchify on Android. Requests are made from Rust withreqwest, so the webview CSP is unchanged.POST /auth/v1/otpwithcreate_user: true. Production's sign-up and login templates both send{{ .Token }}, so new and existing users get a code.POST /auth/v1/verifywithtype: email.POST /auth/v1/logout?scope=local(best effort) on sign-out.POST /rest/v1/rpc/delete_user_accountto delete the account. This is the same RPC Android uses, and it cascades touser_preferencesanddesktop_preferences.com.enaboapps.switchify.pc.account. The access token stays in memory only. Tokens and codes are never logged or sent to the webview, which only receives{available, signedIn, email, pendingEmail}.apikeyheader carries it.Authorizationis sent only with a user access token, because Supabase'ssb_publishable_keys aren't JWTs.get_account,request_sign_in_code,verify_sign_in_code,cancel_sign_in,sign_outanddelete_account. All are main-window-only via the capability, and anaccount-changedevent is emitted on changes.one-time-code, focused automatically), then "Sign in" or "Use a different email"role=alertSWITCHIFY_SUPABASE_URLandSWITCHIFY_SUPABASE_PUBLISHABLE_KEYare baked in at build time withoption_env!, and the macOS and Windows release jobs now require them. Both are set as repository variables; these are public values that the Android app embeds too. Builds without them show "Accounts are unavailable in this build".Review fixes
Keychain write failure during refresh: a refresh token the server has already rotated is kept in memory even if the keychain write fails, so the revoked token is never sent again (which would trigger reuse detection).
Sign-out order: sign-out, delete and a rejected refresh clear the in-memory session first, so a keychain error never leaves "Signed in as …" showing for a deleted account.
Locked keychain at startup: a keychain that can't be read (e.g. locked at login) is retried on the next account call. The UI says so, and sign-in is blocked so it can't silently replace a hidden session. A corrupt entry counts as signed out.
Focus in the Account tab: controls stay enabled while a request is running (
aria-disabled,readOnly, extra presses ignored), so focus is never dropped.Release check: both release jobs require an
https://Supabase URL.Re-review round 2:
aria-disabledbuttons now look disabled.Final review round:
aria-disabledbuttons show as GrayText in forced colours.Validation
npm run lint,npm test(269 passed, 15 newAccountSectiontests) andnpm run buildcargo fmt --checkandcargo clippy --all-targets -D warnings: cleancargo test: 658 passed, 4 ignored, including 19 newaccounttests that use a fake transport and an in-memory store, with no network and no real keychain. They cover:supabase start): the ignored testaccount::tests::local_stack_sign_in_refresh_sign_out_and_deletepassed. It requests a code, reads it from the stack's Mailpit inbox, verifies it, refreshes after a simulated restart and deletes the account through the RPC.apikeyonly (GET /auth/v1/settings), and a bogus refresh token gets HTTP 400, which the code maps to signed out. No real code emails were sent.🤖 Generated with Claude Code