Fix reset-token/session interaction in the auth React layer - #9
Merged
Merged
Conversation
… and an active session skips it entirely Two symptoms traced to one root cause in the auth React layer: 1. After a successful password reset, logging out bounces back to "create a new password" instead of the sign-in screen. 2. Clicking a fresh reset link while already signed in (e.g. from a previous reset, which signs you in on the new password) silently auto-logs in instead of prompting for a new password. Root cause: useSignInForm read `?bool_reset_token=` from the URL on mount and forced mode="newPassword", but never removed it from the URL — and it only ran inside AuthGate's `fallback` branch, which AuthGate picks purely on session presence, with zero awareness of the token. - (1): the token stayed in the URL forever. Signing out clears the session -> AuthGate re-renders `fallback` -> useSignInForm remounts -> its effect re-reads the still-present token -> forces newPassword again. - (2): AuthGate sees `user` truthy and renders `children` immediately; useSignInForm (and its token-reading effect) never mounts, so the token is silently ignored. Fix: lift the reset-token capture into BoolAuthProvider (an ancestor of both AuthGate and useSignInForm) as `pendingResetToken` on context, consumed and stripped from the URL exactly once via a pure, unit-tested helper (`takeResetTokenFromSearch`). AuthGate now forces `fallback` when a reset token is pending, even over an active session — a reset link is an explicit ask to set a new password, so it must always reach that screen. useSignInForm reads the same context value instead of independently reading the URL, and clears it on successful confirmReset or when the visitor backs out of newPassword mode (so AuthGate stops forcing it). react.tsx is unchanged since the initial 0.1.0 release, so this affects every already-created app on the stable ^0.1.0 range, not just the 0.2.0-next canary line. Co-Authored-By: Claude Opus 4.8 <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.
Bugs (reported on a live published Bool)
Root cause
useSignInFormread?bool_reset_token=from the URL on mount and forcedmode = "newPassword"— but never removed it from the URL, and it only ran insideAuthGate'sfallbackbranch.AuthGatepicksfallbackvschildrenpurely on session presence, with zero awareness of the token.AuthGatere-rendersfallback→useSignInFormremounts → its effect re-reads the still-present token → forcesnewPasswordagain.AuthGateseesusertruthy and renderschildrenimmediately;useSignInForm(and its token-reading effect) never mounts, so the token is silently ignored.Fix
Lift the reset-token capture into
BoolAuthProvider(an ancestor of bothAuthGateanduseSignInForm) aspendingResetTokenon context, consumed and stripped from the URL exactly once via a pure, unit-tested helper (takeResetTokenFromSearch).AuthGatenow forcesfallbackwhen a reset token is pending, even over an active session — a reset link is an explicit ask to set a new password, so it must always reach that screen.useSignInFormreads the same context value instead of independently reading the URL, and clears it on successfulconfirmResetor when the visitor backs out ofnewPasswordmode (e.g. "Back to sign in") — soAuthGatestops forcing it afterward.Note on reach
src/react.tsxis unchanged since the initial0.1.0release — this bug has shipped to every already-created app on the stable^0.1.0range, not just the0.2.0-nextentities-canary line currently onmain. Merging this intomainalone won't reach those apps: npm's caret rule on a0.xversion pins the minor, so^0.1.0never resolves to0.2.0-next.x. Flagged separately — no0.1.2backport is included in this PR.Tests
Added unit tests for
takeResetTokenFromSearch(token extraction/stripping while preserving sibling params) — the pure logic the fix depends on. The provider/gate DOM interaction isn't exercisable in this repo's current SSR-only test setup (no jsdom), same boundary the existingreact.test.tsxalready draws.bun run typecheckclean,bun test→ 61 pass (was 57).🤖 Generated with Claude Code