Fix: launch PIN re-triggered the first-run setup wizard every visit (#842)
Regression from the #832 server-side launch gate (Beckid). On a PIN-required, unverified session the gate 401'd /api/setup/status — which the frontend checks BEFORE the PIN screen. The 401 left setup_complete undefined, so `!undefined` relaunched the full setup wizard on every visit (cancel → PIN screen worked, which was the tell). Two-layer fix: - Allowlist /api/setup/status in the launch gate (it's a harmless boolean, no secrets) so the frontend gets the real answer even while locked. - Make the frontend fail-safe: only launch the wizard on a definitive setup_complete === false from an OK response — never on a 401/locked/ambiguous one. Test: locked session still 401s data endpoints but /api/setup/status returns {setup_complete:true}; added a gate-allowlist assertion. 21 gate tests pass.
This commit is contained in:
parent
7856433c6f
commit
355b39e3b5
3 changed files with 19 additions and 4 deletions
|
|
@ -30,6 +30,9 @@ from __future__ import annotations
|
|||
_ALLOWED_GET = frozenset({
|
||||
'/api/profiles', # profile picker list (multi-profile launch)
|
||||
'/api/profiles/current', # how the frontend detects the lock state
|
||||
'/api/setup/status', # #842: first-run check runs before the PIN screen;
|
||||
# blocking it made the frontend think setup wasn't
|
||||
# done and re-launch the wizard on every visit.
|
||||
})
|
||||
|
||||
# POST endpoints that drive selection + unlock. Selecting a profile only sets
|
||||
|
|
|
|||
|
|
@ -77,6 +77,12 @@ def test_locked_allows_unlock_flow():
|
|||
assert L('/api/profiles/logout', 'POST') is False
|
||||
|
||||
|
||||
def test_locked_allows_setup_status():
|
||||
# #842: the first-run check runs before the PIN screen. Blocking it made the
|
||||
# frontend think setup was incomplete and relaunch the wizard every visit.
|
||||
assert L('/api/setup/status', 'GET') is False
|
||||
|
||||
|
||||
def test_locked_allows_keyauthed_public_api():
|
||||
# The public REST API carries its own @require_api_key, so a launch-locked
|
||||
# UI must not break a legitimate headless key holder.
|
||||
|
|
|
|||
|
|
@ -2072,11 +2072,17 @@ document.addEventListener('DOMContentLoaded', async function () {
|
|||
if (!forceSetup) {
|
||||
try {
|
||||
const setupResp = await fetch('/api/setup/status');
|
||||
// Fail-safe (#842): only launch the wizard when the server DEFINITIVELY
|
||||
// says setup isn't done. A non-OK response (e.g. 401 while the launch
|
||||
// PIN is locked) must NOT trigger the wizard — otherwise a PIN-gated
|
||||
// returning user gets the full setup flow every visit.
|
||||
if (setupResp.ok) {
|
||||
const setupData = await setupResp.json();
|
||||
if (!setupData.setup_complete) {
|
||||
if (setupData.setup_complete === false) {
|
||||
showWizard = true;
|
||||
localStorage.removeItem('soulsync_setup_complete');
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Setup status check failed, continuing normal init:', e);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue