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

@@ -175,15 +175,15 @@ class TestDatabaseMigrations:
engine = db.get_engine()
with engine.connect() as conn:
# Check migrations were recorded correctly (001, 002, and 003)
# Check migrations were recorded correctly (001-005)
result = conn.execute(text("SELECT COUNT(*) FROM migrations"))
count = result.fetchone()[0]
assert count == 3
assert count == 5
# Verify all migrations are present
result = conn.execute(text("SELECT version FROM migrations ORDER BY version"))
versions = [row[0] for row in result]
assert versions == [1, 2, 3]
assert versions == [1, 2, 3, 4, 5]
def test_initialize_full_setup(self):
"""Test initialize performs full database setup."""
@@ -261,6 +261,7 @@ class TestMigrationSchemaCorrectness:
"created_at",
"verified_at",
"two_factor",
"last_checked", # Added in migration 005
}
assert columns == expected_columns