auth: replace UUID-based login with JWT credential verification

Login now requires login/email + password verified against DB via
/api/auth/login. Only approved registrations can access the app.
Signal endpoint accepts JWT Bearer tokens alongside legacy api_key auth.
Old UUID-only registration flow removed from frontend.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Gros Frumos 2026-03-21 14:14:12 +02:00
parent 1adcabf3a6
commit 04f7bd79e2
8 changed files with 173 additions and 128 deletions

View file

@ -222,9 +222,9 @@ def test_html_loads_app_js() -> None:
assert "/app.js" in _html()
def test_html_has_name_input() -> None:
"""index.html must have name input field for onboarding."""
assert 'id="name-input"' in _html()
def test_html_has_login_input() -> None:
"""index.html must have login input field for onboarding."""
assert 'id="login-input"' in _html()
# ---------------------------------------------------------------------------
@ -316,31 +316,19 @@ def _app_js() -> str:
return (FRONTEND / "app.js").read_text(encoding="utf-8")
def test_app_uses_crypto_random_uuid() -> None:
"""app.js must generate UUID via crypto.randomUUID()."""
assert "crypto.randomUUID()" in _app_js()
def test_app_posts_to_auth_login() -> None:
"""app.js must send POST to /api/auth/login during login."""
assert "/api/auth/login" in _app_js()
def test_app_posts_to_api_register() -> None:
"""app.js must send POST to /api/register during onboarding."""
assert "/api/register" in _app_js()
def test_app_posts_to_auth_register() -> None:
"""app.js must send POST to /api/auth/register during registration."""
assert "/api/auth/register" in _app_js()
def test_app_register_sends_uuid() -> None:
"""app.js must include uuid in the /api/register request body."""
app = _app_js()
# The register call must include uuid in the payload
register_section = re.search(
r"_apiPost\(['\"]\/api\/register['\"].*?\)", app, re.DOTALL
)
assert register_section, "No _apiPost('/api/register') call found"
assert "uuid" in register_section.group(0), \
"uuid not included in /api/register call"
def test_app_uuid_saved_to_storage() -> None:
"""app.js must persist UUID to storage (baton_user_id key)."""
assert "baton_user_id" in _app_js()
def test_app_stores_auth_token() -> None:
"""app.js must persist JWT token to storage."""
assert "baton_auth_token" in _app_js()
assert "setItem" in _app_js()
@ -434,16 +422,14 @@ def test_app_posts_to_api_signal() -> None:
assert "/api/signal" in _app_js()
def test_app_signal_sends_user_id() -> None:
"""app.js must include user_id (UUID) in the /api/signal request body."""
def test_app_signal_sends_auth_header() -> None:
"""app.js must include Authorization Bearer header in /api/signal request."""
app = _app_js()
# The signal body may be built in a variable before passing to _apiPost
# Look for user_id key in the context around /api/signal
signal_area = re.search(
r"user_id.*?_apiPost\(['\"]\/api\/signal", app, re.DOTALL
r"_apiPost\(['\"]\/api\/signal['\"].*Authorization.*Bearer", app, re.DOTALL
)
assert signal_area, \
"user_id must be set in the request body before calling _apiPost('/api/signal')"
"Authorization Bearer header must be set in _apiPost('/api/signal') call"
def test_app_sos_button_click_calls_handle_signal() -> None:
@ -456,15 +442,15 @@ def test_app_sos_button_click_calls_handle_signal() -> None:
"btn-sos must be connected to _handleSignal"
def test_app_signal_uses_uuid_from_storage() -> None:
"""app.js must retrieve UUID from storage (_getOrCreateUserId) before sending signal."""
def test_app_signal_uses_token_from_storage() -> None:
"""app.js must retrieve auth token from storage before sending signal."""
app = _app_js()
handle_signal = re.search(
r"async function _handleSignal\(\).*?^}", app, re.MULTILINE | re.DOTALL
)
assert handle_signal, "_handleSignal function not found"
assert "_getOrCreateUserId" in handle_signal.group(0), \
"_handleSignal must call _getOrCreateUserId() to get UUID"
assert "_getAuthToken" in handle_signal.group(0), \
"_handleSignal must call _getAuthToken() to get JWT token"
# ---------------------------------------------------------------------------