feat: bind free sessions to accounts to prevent multi-account abuse - #1171
feat: bind free sessions to accounts to prevent multi-account abuse#1171AbhijitK20 wants to merge 3 commits into
Conversation
Prevent users from switching accounts mid-session to abuse free-tier limits. When a freebuff session becomes active, it is now bound to the authenticating user's ID. Multiple guard rails enforce this: - /logout blocked during active session (use /end-session or --force) - Login rejected if new user differs from session-bound user - Startup guard ends session if credentials changed externally - Session binding cleared on ended/none/superseded transitions The server already enforces one free session per account; this adds client-side enforcement so the same machine can't cycle through multiple accounts to farm free sessions.
…d tests - Login modal now shows clear error message when rejected due to account mismatch instead of silently reverting to the login screen - /logout --force now releases the server-side session slot and clears the binding before clearing credentials - Added ACCOUNT_SWITCH_BLOCKED analytics event to track abuse attempts - Added unit tests for session binding store and helper functions Addresses review feedback on PR CodebuffAI#1171 for tighter abuse prevention.
d8e4630 to
792fcc1
Compare
|
Good instinct — preventing account-cycling on shared free-tier slots is worth doing — but the implementation doesn't actually hold up against the scenario it's designed to stop.
That makes the added friction (blocking Secondary point: Tests only cover the zustand store getters/setters, not the actual restart-bypass scenario, which is the crux of whether this defends anything. Worth revisiting with persisted or server-verified binding rather than closing outright, since the direction is sound. |
Addresses bot feedback on PR CodebuffAI#1171: the in-memory session binding was bypassed by simply restarting the CLI process. Now the binding is persisted to ~/.config/manicode/session-binding.json alongside the session lifecycle: - Persisted when session becomes active - Loaded on startup and restored to in-memory state - Cleared when session ends or on force-logout - Checked on startup against current credentials This closes the restart-bypass abuse path: a user who Ctrl-C's and restarts with different credentials will hit the startup guard, which reads the persisted binding and ends the session if the user changed.
|
Thanks for the thorough review — the restart-bypass hole was a real catch. Here's what's been addressed: 1. Persistence (the main gap)Added
The restart-bypass path is now closed: 2.
|
Summary
Prevents users from switching accounts mid-session to abuse free-tier limits. When a freebuff session becomes active, it is now bound to the authenticating user's ID. Multiple guard rails enforce this:
Changes
cli/src/state/freebuff-session-store.tssessionBoundUserIdstate + settercli/src/hooks/use-freebuff-session.tsactive, clear onended/none/superseded, startup guard for mismatched userscli/src/commands/command-registry.ts/logoutblocked during active session (requires--end-sessionor--force)cli/src/hooks/use-auth-state.tscli/src/data/slash-commands.ts/logouthelp text to document--forceHow it works
Session binds on activation — When the poll loop receives an
activestatus,sessionBoundUserIdis set to the current user's ID fromcredentials.json./logoutblocked — If a session is bound,/logoutshows a warning and suggests/end-sessionfirst. Use/logout --forceto override.Login rejected on mismatch — If someone logs in as a different user while a session is bound to another account, the login is rejected and credentials are cleared.
Startup guard — On mount, if
sessionBoundUserIdexists but doesn't match the current credentials (e.g., credentials were swapped externally), the session is ended immediately.Binding cleared on session end — When the session transitions to
ended,none, orsuperseded, the binding is cleared.Abuse scenario prevented
Notes
--forceflag on/logoutprovides an escape hatch for edge cases