Implement persistent API-key authentication

Protect the research lifecycle with X-API-Key validation backed by persistent user and key records. Store only salted scrypt hashes, support expiry and revocation, and expose a local admin CLI for create/list/revoke workflows.

Initialize only the authentication schema at startup, prevent SQL echo from exposing sensitive bound values, and keep health probes public. Add coverage for valid, missing, invalid, expired, and revoked keys.

Document deployment and key administration, update the local CLI to send NSCT_API_KEY, and record the reset handoff state.
This commit is contained in:
faligam
2026-09-06 17:23:05 +02:00
parent 64eb4e8465
commit 1aacf4aa20
14 changed files with 1065 additions and 60 deletions

View File

@@ -8,7 +8,7 @@ import uuid
from datetime import datetime, timezone
from typing import Any
from fastapi import BackgroundTasks, APIRouter, HTTPException
from fastapi import BackgroundTasks, APIRouter, Depends, HTTPException
from pydantic import BaseModel, Field
from nsct.logging_config import set_request_ctx, clear_request_ctx, set_research_run_id, get_logger
@@ -26,10 +26,13 @@ from nsct.metrics import (
H_RESEARCH_DURATION,
G_ACTIVE_RESEARCH_RUNS,
)
from nsct.security.api_keys import require_api_key
logger = logging.getLogger(__name__)
router = APIRouter()
# Research data and operations are intentionally never exposed without a user
# API key. Health/readiness endpoints remain public for infrastructure probes.
router = APIRouter(dependencies=[Depends(require_api_key)])
# ---------------------------------------------------------------------------
@@ -674,4 +677,4 @@ async def delete_research(research_id: str) -> dict[str, str]:
run.state = ResearchRunState.CANCELLED.value
run.updated_at = _now()
del _research_store[research_id]
return {"status": "deleted", "research_id": research_id}
return {"status": "deleted", "research_id": research_id}