Phase 4 enhancements: WebSocket auth, HTTPS UI, plugin hooks, build fixes

Security:
- Add token-based WebSocket authentication (closes critical security gap)
  - In-memory token store with 24h TTL (token_store.py)
  - POST /api/auth/token exchanges Basic Auth for WS token
  - GET /api/auth/status public endpoint for auth check
  - WebSocket validates token query param, rejects with close code 4401
  - Frontend LoginPrompt modal for credential entry
  - WebSocket manager handles full auth flow with auth_required state
  - No-op when AUTH_ENABLED=false (preserves existing behavior)

HTTPS:
- Wire HTTPS toggle in Settings UI (POST /api/system/ssl/toggle)
- Add certificate regeneration button
- Display SSL info (expiration, SANs, SHA256 fingerprint)

Plugins:
- Wire trigger_hook("pm3_command") in PM3 service
- Wire trigger_hook("update_check") in update manager

Build/Infrastructure:
- Enable NetworkManager in pi-gen AP setup stage
- Add HF booster board detection patch for Proxmark3
- Update LED PWM control patch
- Fix BLE adapter, UPS drivers, WiFi manager improvements
- Update HTTPS support stage script

Documentation:
- Update PROJECT_STATUS.md and IMPLEMENTATION_PRIORITIES.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
michael
2026-03-03 11:45:11 -08:00
parent 4f35df1781
commit 2ec89041ef
24 changed files with 1249 additions and 362 deletions

View File

@@ -1,11 +1,13 @@
"""Authentication module for Dangerous Pi API."""
import secrets
from fastapi import Depends, HTTPException, status
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from .. import config
from .token_store import create_token
security = HTTPBasic()
router = APIRouter()
def verify_credentials(credentials: HTTPBasicCredentials = Depends(security)) -> str:
@@ -61,3 +63,26 @@ def get_optional_auth(credentials: HTTPBasicCredentials = Depends(security)) ->
return verify_credentials(credentials)
except HTTPException:
return None
@router.get("/status")
async def auth_status():
"""Check whether authentication is enabled.
This endpoint is always public so the frontend can determine
whether to prompt for credentials before connecting the WebSocket.
"""
return {"auth_enabled": config.AUTH_ENABLED}
@router.post("/token")
async def get_auth_token(username: str = Depends(verify_credentials)):
"""Exchange Basic Auth credentials for a WebSocket auth token.
The returned token should be passed as a query parameter when
connecting to the WebSocket endpoint: ws://host/ws/events?token=...
Tokens expire after 24 hours.
"""
token = create_token(username)
return {"token": token}