Cap device-code poll interval and sweep expired device codes
Two more fixes from the device-flow security review: slow_down interval grew without bound (OidcController): - Each too-fast poll bumped the persisted interval by 5s with no ceiling, so a client polling slightly fast — or an attacker spamming a known device_code — could balloon it (5→10→15→…) past the 10-minute expiry and starve a legitimate client of its token. Clamp the bump to OidcDeviceCode::MAX_INTERVAL (30s); a client polling at the advertised interval is never throttled. Expired device codes accumulated forever (OidcTokenCleanupJob): - Anonymous callers can create device codes via /oauth/device_authorization, and expired/denied/abandoned rows were never reaped. Sweep rows past expiry (with a 1-hour grace to stay clear of in-flight redemption); redeemed codes are already destroyed at token issuance. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Q4ATZHoMCWqvSpE2yYoie
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
017dfdff0e
commit
64410a0c50
@@ -119,6 +119,14 @@ class OidcDeviceFlowControllerTest < ActionDispatch::IntegrationTest
|
||||
assert_equal "slow_down", JSON.parse(@response.body)["error"]
|
||||
end
|
||||
|
||||
test "slow_down interval is capped and does not grow without bound" do
|
||||
dc = OidcDeviceCode.create!(application: @cli, scope: "openid")
|
||||
# Hammer the code far more times than it would take to exceed the cap if the
|
||||
# interval grew by 5 unbounded (20 * 5 = 100s >> MAX_INTERVAL).
|
||||
20.times { poll(dc) }
|
||||
assert_operator dc.reload.interval, :<=, OidcDeviceCode::MAX_INTERVAL
|
||||
end
|
||||
|
||||
test "token endpoint returns expired_token for an expired code" do
|
||||
dc = OidcDeviceCode.create!(application: @cli, scope: "openid", expires_at: 1.minute.ago)
|
||||
poll(dc)
|
||||
|
||||
Reference in New Issue
Block a user