feat: implement Phase 1 hardening

- Verify JWT signatures via JWKS in auth.py
- Fix broken frontend auth button references
- Add Pydantic Settings for env validation (RETENTION_DAYS, CORS_ORIGINS)
- Create MongoDB indexes + TTL on startup
- Add /health endpoint and CORS middleware
- Escape regex input in event queries
- Fix dedupe() return calculation in maintenance.py
- Replace basic logging with structured structlog JSON logs
- Update README and add ROADMAP.md
This commit is contained in:
2026-04-14 11:48:29 +02:00
parent f9f1399f57
commit 4f6e16d64d
12 changed files with 392 additions and 46 deletions

View File

@@ -1,10 +1,11 @@
import time
import logging
import structlog
from typing import Optional, Set
import requests
from fastapi import Depends, HTTPException, Header
from jose import jwt
from jose.jwk import construct
from config import (
AUTH_ENABLED,
@@ -15,7 +16,7 @@ from config import (
)
JWKS_CACHE = {"exp": 0, "keys": []}
logger = logging.getLogger("aoc.auth")
logger = structlog.get_logger("aoc.auth")
def _get_jwks():
@@ -48,9 +49,18 @@ def _allowed(claims: dict, allowed_roles: Set[str], allowed_groups: Set[str]) ->
def _decode_token(token: str, jwks):
try:
# Unverified decode to accept tokens from single-app setups without strict signing validation.
claims = jwt.get_unverified_claims(token)
header = jwt.get_unverified_header(token)
kid = header.get("kid")
key_dict = next((k for k in jwks if k.get("kid") == kid), None)
if not key_dict:
raise HTTPException(status_code=401, detail="Invalid token: signing key not found")
key = construct(key_dict)
decode_kwargs = {"algorithms": ["RS256"]}
if AUTH_CLIENT_ID:
decode_kwargs["audience"] = AUTH_CLIENT_ID
claims = jwt.decode(token, key, **decode_kwargs)
tid = claims.get("tid")
iss = claims.get("iss", "")
if AUTH_TENANT_ID and tid and tid != AUTH_TENANT_ID:
@@ -61,7 +71,7 @@ def _decode_token(token: str, jwks):
except HTTPException:
raise
except Exception as exc:
logger.warning("Token parse failed: %s", exc)
logger.warning("Token verification failed", error=str(exc))
raise HTTPException(status_code=401, detail="Invalid token")