From c0170311c90f2e4ac819e867dfa0dd5a33845b9d Mon Sep 17 00:00:00 2001 From: Aakash Kattelu Date: Mon, 20 Jul 2026 10:23:00 -0700 Subject: [PATCH] fix(honcho): default device-code poll interval to 5s when AS omits it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RFC 8628 §3.2 makes the device-authorization `interval` optional with a client-side default of 5 seconds. request_device_code required it, so a compliant AS that omitted it hit the malformed-response path and the flow could never complete. Fall back to 5s and cover it with a regression test. Co-Authored-By: Claude Fable 5 --- plugins/memory/honcho/oauth_flow.py | 3 ++- tests/honcho_plugin/test_oauth_flow.py | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/plugins/memory/honcho/oauth_flow.py b/plugins/memory/honcho/oauth_flow.py index e168364e1a33..b1268c02d441 100644 --- a/plugins/memory/honcho/oauth_flow.py +++ b/plugins/memory/honcho/oauth_flow.py @@ -465,7 +465,8 @@ def request_device_code( f"{verification_uri}?user_code={body['user_code']}", ), expires_in=int(body["expires_in"]), - interval=int(body["interval"]), + # RFC 8628 §3.2: interval is optional; clients default to 5s. + interval=int(body.get("interval", 5)), ) except (KeyError, TypeError, ValueError) as e: raise DeviceFlowError( diff --git a/tests/honcho_plugin/test_oauth_flow.py b/tests/honcho_plugin/test_oauth_flow.py index a52941a8e60d..5a63b8fcdd79 100644 --- a/tests/honcho_plugin/test_oauth_flow.py +++ b/tests/honcho_plugin/test_oauth_flow.py @@ -353,6 +353,24 @@ def test_request_device_code_parses_response_and_sends_identity(fake_as): assert _FakeAS.last_device_form["source"] == "hermes-cli" +def test_request_device_code_defaults_interval_when_omitted(monkeypatch): + # RFC 8628 §3.2: interval is optional; a compliant AS may omit it, and the + # client must fall back to 5s rather than treating the response as malformed. + endpoints = oauth_flow.resolve_endpoints(environment="local") + monkeypatch.setattr( + oauth, + "_http_post_form_status", + lambda *a, **k: (200, { + "device_code": "dev-code-1", + "user_code": "ABCD-EFGH", + "verification_uri": "http://localhost:3000/device", + "expires_in": 600, + }), + ) + device = oauth_flow.request_device_code(endpoints) + assert device.interval == 5 + + def test_full_device_flow_pending_then_approved(tmp_path, fake_as): _FakeAS.device_responses = ["authorization_pending", "authorization_pending", "ok"] config_path = tmp_path / "honcho.json"