fix(auth): require email authentication every login

CRITICAL SECURITY FIX:
- Email code required EVERY login (authentication, not verification)
- DNS TXT check cached separately (domain verification)
- New auth_sessions table for per-login state
- Codes hashed with SHA-256, constant-time comparison
- Max 3 attempts, 10-minute session expiry
- OAuth params stored server-side (security improvement)

New files:
- services/auth_session.py
- migrations 004, 005
- ADR-010: domain verification vs user authentication

312 tests passing, 86.21% coverage

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-22 15:16:26 -07:00
parent 9b50f359a6
commit 9135edfe84
17 changed files with 3457 additions and 529 deletions

View File

@@ -5,6 +5,7 @@ from gondulf.config import Config
from gondulf.database.connection import Database
from gondulf.dns import DNSService
from gondulf.email import EmailService
from gondulf.services.auth_session import AuthSessionService
from gondulf.services.domain_verification import DomainVerificationService
from gondulf.services.happ_parser import HAppParser
from gondulf.services.html_fetcher import HTMLFetcherService
@@ -111,3 +112,17 @@ def get_token_service() -> TokenService:
token_length=32, # 256 bits
token_ttl=config.TOKEN_EXPIRY # From environment (default: 3600)
)
# Auth Session Service (for per-login authentication)
@lru_cache
def get_auth_session_service() -> AuthSessionService:
"""
Get AuthSessionService singleton.
Handles per-login authentication via email verification.
This is separate from domain verification (DNS check).
See ADR-010 for the architectural decision.
"""
database = get_database()
return AuthSessionService(database=database)