Skip to content

Device login: wrong codes from one network no longer lock out its other users - #886

Merged
schmitch merged 2 commits into
mainfrom
claude/803-device-code-network-budget
Nov 3, 2026
Merged

schmitch merged 2 commits into
mainfrom
claude/803-device-code-network-budget

Conversation

@schmitch

@schmitch schmitch commented Nov 3, 2026

Copy link
Copy Markdown
Member

Fixes #803

What changed

  • Separate network budget. Wrong user codes on /device were counted per client network in the same throttle as the per-session ones, ten per five minutes. That meant one user's typos, or a page opening /device?code=… ten times for a signed-in victim, locked every user behind that NAT out. Without auth.trusted_proxy_cidrs, it locked out every user of the install. The network now has its own throttle, AppState::device_network_throttle, allowing DEVICE_CODES_PER_NETWORK = 100 wrong codes per five minutes. That is still far too few to guess a code out of 20^8. The per-session budget (device_code_throttle) stays at ten per five minutes. A 429's Retry-After comes from whichever throttle refused.
  • A linked code is looked up only after a click. /device?code=… (web/src/routes/Device.tsx) now only fills the code in. The page says "Check that this is the code your terminal shows, then continue." and runs the lookup when the user clicks Continue. So a page opened by someone else spends neither the session's budget nor the network's. Approval still needs the click on Approve, as before.
  • Docs. docs/deployment.md (Device login, Guessing and The /device page) states both limits and the click.

Tests

  • crates/server/tests/device_auth.rs:
    • New wrong_codes_in_one_session_leave_the_network_its_other_sessions: two sessions from one address. Ten wrong codes in one cut that session off with 429, and the other session still looks the code up (200) and approves it (204).
    • New a_network_is_cut_off_after_a_hundred_wrong_codes: ten sessions × ten wrong codes from one address. A fresh session of that network then gets 429 with Retry-After, and another network still finds the code.
    • guessing_a_code_is_cut_off_by_the_rate_limit now expects the network's other session to find the code instead of being refused.
    • Without the server fix (the three source files reset to origin/main), all three fail: left: 429, right: 200 for the colleague's session, and 429 instead of 404 before the hundredth code. With the fix, all 13 tests in the file pass.
  • web/src/routes/Device.test.tsx:
    • New test: a linked code is filled in but sends no lookup until Continue is clicked.
    • The existing linked-code tests (deny, sign-in redirect, 404, expired before the click) now click Continue first.
  • Checks run: cargo fmt --all --check, cargo clippy -p forge-server --all-targets -- -D warnings, cargo test -p forge-server --lib and --test device_auth (Postgres pulled through mirror.gcr.io), pnpm run tsc, pnpm run test (622 passed), cargo xtask changelog --check.

Changelog

docs/changes/2.2.0/803.fixed.md


Generated by Claude Code

…er users

Wrong user codes were counted per client network with the session's
budget, ten per five minutes, so one user's typos, or a page opening
/device?code=… for a signed-in victim, locked every user of the network
(of the install, behind an untrusted proxy) out of /device. The network
now has a throttle of its own, a hundred wrong codes per five minutes;
the session's stays at ten. The /device page looks a filled-in code up
only once the user clicks Continue.

Fixes #803

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012e6DJbn62iKFkhRepaeRCW
@schmitch
schmitch enabled auto-merge November 3, 2026 23:01

schmitch commented Nov 3, 2026

Copy link
Copy Markdown
Member Author

CI test failed in crates/db/tests/encryption.rs: dek_revocation_is_checked_at_commit_time_against_writers (unwrap_err() on Ok). This PR didn't cause it. The PR doesn't touch forge-db, and the same test failed the same way on main at 5812f2b (run 41734827738). The race in that test is #883, and #885 fixed it on main.

I merged main into this branch (512d0b3) to bring in #885. Locally, that test file (13 tests) and device_auth (13 tests) pass, and cargo xtask changelog --check passes.


Generated by Claude Code

@schmitch
schmitch added this pull request to the merge queue Nov 3, 2026
Merged via the queue into main with commit dc22e70 Nov 3, 2026
20 checks passed
@schmitch
schmitch deleted the claude/803-device-code-network-budget branch November 3, 2026 23:42
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.

Device login: ten wrong codes from one network lock every user of that network out of /device, and a linked code counts without a click

2 participants