fix(honcho): default device-code poll interval to 5s when AS omits it

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 <noreply@anthropic.com>
This commit is contained in:
Aakash Kattelu 2026-07-20 10:23:00 -07:00 committed by Teknium
parent 2aa359ea70
commit c0170311c9
2 changed files with 20 additions and 1 deletions

View file

@ -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(

View file

@ -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"