mirror of
https://github.com/ruvnet/RuView
synced 2026-07-24 17:43:20 +00:00
f7cc68bd5c
Found by an empirical route sweep during the pre-merge pass, not by a test.
The step-up gate fired BEFORE the scope check, so a caller holding only
`sensing:read` who attempted a privileged action with a session older than
ADMIN_REVERIFY_SECS received the RFC 6750 "reauthentication required"
challenge. `api.service.js` acts on that by redirecting through /oauth/start —
and the user returns with exactly the same scopes and is refused again. One
wasted round trip, and a reason for the refusal that was simply untrue: they
were not refused for staleness, they were refused for capability.
Now the scope check comes first, so only a caller who actually HOLDS
`sensing:admin` is ever asked to prove the session is fresh. Not a loop before
(the second refusal is a plain 401 with no challenge) and not a security issue
either way — both paths refuse. It was misleading, and it cost a redirect.
Also confirms the deny-by-default gate does not over-reach. Probed the real
binary with auth off vs on:
/, /ui/*, /health, /health/{live,ready,metrics,version},
/oauth/{status,start} unchanged
/api/field, /api/v1/* 200 -> 401 (intended)
/metrics, /favicon.ico 404 -> 401 (never existed; now uniform)
Nothing that returned 2xx before returns 401 now, which is the property that
matters for existing deployments.
Tests: +2. `a_read_only_user_is_not_sent_to_reauthenticate_pointlessly` fails
against the old ordering with the challenge header present; its counterpart
`an_admin_holder_with_a_stale_session_does_get_the_challenge` ensures the fix
does not simply disable the signal.
Verified: workspace 176 suites clean, ruview-auth 62+25+2 --all-features.
Co-Authored-By: Ruflo & AQE