feat: headless device-code login for servers and Docker (#96)#99
Conversation
…login # Conflicts: # src/auth/mod.rs
|
@claude review |
|
Claude finished @dubadub's task in 2m 26s —— View job Code Review
Overall this is a clean RFC 8628 implementation. The separation of the pure Robustness1. Unbounded interval growth on PollOutcome::SlowDown => interval += Duration::from_secs(5),RFC 8628 says to increase the interval by 5s per PollOutcome::SlowDown => interval = (interval + Duration::from_secs(5)).min(Duration::from_secs(30)),2. Zero-interval guard — If the server returns let actual_interval = interval.max(Duration::from_secs(5));
_ = tokio::time::sleep(actual_interval) => {}Design / duplication3.
4. Fresh let client = reqwest::Client::new();
Tests5. Missing test: The test #[test]
fn headless_flag_works_on_non_linux() {
assert!(prefer_device_flow_from(true, false, false, true));
}Minor / nits6. Unreachable match self.get_session() {
Some(session) => println!("Logged in as {}", session.email.unwrap_or(session.user_id)),
None => println!("Logged in"), // logically unreachable: set_session just succeeded
}
7. Redundant
SummaryThe RFC 8628 state machine is correct, the test coverage is solid for the interpreter and flow-selection matrix, and the Ctrl+C cancellation is idiomatic. The items worth addressing before merge are #1 (unbounded slow_down interval), #3 (duplicated Docker detection), and #5 (incomplete test coverage for the headless flag on non-Linux). The rest are low-priority polish. |
Summary
Adds OAuth 2.0 device-authorization-grant login (RFC 8628) so users can authenticate
cook-syncon a headless server or in Docker, where the existing browser/loopback flow can't run. Ported from cookcli's implementation; the cook.md server already exposes the endpoints (POST /oauth/device/code,POST /oauth/device/token).src/auth/device_flow.rs: device-code request, RFC 8628 token-polling state machine (pure, unit-tested interpreter + networked loop with per-request timeouts),client_name/server_host_labelhelpers.AuthManager::device_login(): prints a verification URL + user code, best-effort opens the browser, polls with a progress indicator and Ctrl-C cancellation, then converges on the existingset_session().cook-sync logingains--headless. Flow selection:--headlessalways uses device flow; otherwise it auto-detects (Docker, or Linux with noDISPLAY/WAYLAND_DISPLAY); else the browser flow runs unchanged. macOS/Windows only auto-trigger via the flag.Closes #96.
Notes / out of scope
cook-sync start(the daemon) still requires a system tray and won't run on a truly headless host — headless login is delivered here; headless daemon operation should be a separate follow-up issue.Test Plan
cargo test— 102 + 12 pass (10 device-flow interpreter tests, 6 flow-selection matrix tests)cargo clippy --all-targets -- -D warnings -A dead_code— cleancargo fmt --check— cleancook-sync login --helpshows--headlesscook-sync login --headlesson a server → enter code at the URL → session saved and sync works