Conversation
The enrollment wizard previously required users to click Copy AND Download AND retype two codes at random positions before letting them past the save-codes step. KEEP-640 #4 calls that out as too heavy and asks for Download as the sole gate. - Backup-codes panel: drop the retype-at-position confirmation block and the codes-hidden state. Render codes throughout the panel; expose Copy as an optional ghost button; gate the "I've saved my codes" CTA on Download alone. - Setup dialog: update the lock-error toasts so they ask the user to download (not "copy and confirm two") before closing. Lock-on-close stays intact: the dialog still refuses to close while codes are visible and the user has not confirmed.
The DualFactorSteps wizard renders a Cancel button on the email phase that fires the consumer's onBack prop. Most consumers wired that to "step back one phase inside the same modal" (setState, setPhase, setMode), so Cancel from inside the MFA prompt dropped the user back at the input form instead of closing the modal. KEEP-640 #1 calls that out as broken. Switch each consumer's onBack to fully close its wrapping modal: - withdraw-modal: closeAll() instead of setState("input"). The user aborts the whole withdrawal flow rather than re-editing amount/recipient inside an MFA prompt they just bailed on. - api-keys-overlay (create-key path): pop() instead of setPhase("label"). Same shape as the existing pop-on-Cancel on the export-private-key flow. - delete-account-section: setOpen(false) + resetAll() instead of setPhase("confirmation"). Closes the AlertDialog entirely. - totp-manage-dialog (disable flow): onOpenChange(false) so Cancel aborts the disable, rather than dropping back to the summary listing where a second Cancel is needed to leave. The locked enroll/verify gate pages (/enroll-mfa, /verify-mfa) keep their hard-locked behavior; the user has no session to fall back to in those cases, so Cancel cannot be a no-op there. The change-password-section panel stays on setPhase("passwords") because it is an inline section on the settings page, not a modal -- Cancel there is "back to the password form", which is the expected shape for an inline wizard.
When a user has more than one OAuth provider linked (Google + GitHub) and they try to reset a password they do not have, the "this account uses social login" reminder previously named whichever provider appeared first in OAUTH_PROVIDERS. With the constant ordered ["github", "google"], a Google user who had also linked GitHub would receive an email saying "your account uses GitHub". KEEP-640 #7. Sort the linked OAuth accounts by accounts.updated_at DESC and pick the most recently used. Better Auth bumps updated_at on every successful sign-in via that provider, so this matches the provider the user just attempted to use.
Forward port of the same fix in PR #1367. The session.create.before hook in lib/auth.ts stamps requires_mfa = true on every fresh session of a user with two_factor_enabled. That gate is for the legacy single-factor sign-in path; strict-signin verifies password + email OTP + TOTP atomically before the session is minted, so the freshly minted session is already fully MFA-verified. Without this clear, the proxy gate sees requires_mfa = true on the new session right after sign-in and 302s the user to /verify-mfa, where they re-enter both codes for nothing. KEEP-640 #5. Same shape as /api/user/totp/enroll: UPDATE sessions SET requires_mfa = false, mfa_verified_at = now() scoped by user_id.
If Better Auth's OAuth callback returns a redirect without the session cookie we expect, interceptOauthCallback silently bails and returns the response untouched. That path is implicated in the "signed in with Google but no session on KeeperHub" reports that triggered KEEP-640 #2/#3. Add a single warn log so we can tell next time whether the Set-Cookie array was empty, missing the session_token name, or the runtime did not expose getSetCookie. Diagnostic only; behaviour unchanged.
Iterating on KEEP-640 #4 with feedback during testing. The wizard was three sequential phases (Scan -> Verify -> Save codes); fold the first two into a single "Scan & verify" step with the QR, setup-key chip, and TOTP input all on one screen, and rename the second to "Download codes". Step 2 is now opt-in: downloading is not required to close the dialog. A "Skip for now" button on the parent footer is the only thing that closes step 2; the codes can be regenerated from settings later. - totp-setup-dialog.tsx: - Phase list shrinks from three to two; STEP_DEFS reflects the new labels. - Setup-key row is now a single line: label + clickable chip with the manual-entry key in monospace + ghost copy button. Clicking the chip or the button calls handleCopyKey; a 3-second keyJustCopied flag flashes the chip border emerald so it's clear which control fired. Both chip and button are h-7 so they sit flush. - The "verify" phase merges into the setup phase: the TOTP input renders below the setup key on the same screen, and Continue calls /enroll directly. - Step 2 strips the redundant alert + verbose description; one short sentence covers what the codes are for. - totp-backup-codes-panel.tsx: - Drop the now-redundant onConfirmed prop. Panel is purely a display + Copy + Download component; closing the dialog is the parent's responsibility via an explicit Skip / Done. - Drop the Alert that duplicated the parent's description. - totp-manage-dialog.tsx: - The viewing-codes mode (regenerate flow) now gets its own Done button in a DialogFooter, since the panel no longer emits onConfirmed. No backend changes: /api/user/totp/setup still issues TOTP secret + manual entry key, /api/user/totp/enroll still verifies the code and returns the backup codes.
Removing the prod console.warn in interceptOauthCallback that logged when extractSessionToken returned null. Without a repro it was noise; if the OAuth-no-session reports resurface we can wire proper telemetry instead of an inline log. Also dropping the StepIndicator JSDoc block in totp-setup-dialog; the function name and STEP_DEFS are self-evident.
fix(auth): login flow bug sweep
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.
No description provided.