Files
Aurora-Core/pyproject.toml
T
NixReaperandClaude Sonnet 5 7bee596a0e
CI / lint (push) Canceled after 0s
CI / typecheck (push) Canceled after 0s
CI / unit-tests (push) Canceled after 0s
CI / integration-tests (push) Canceled after 0s
CI / container-build (push) Canceled after 0s
CI / dependency-audit (push) Canceled after 0s
Add user accounts: registration, login, and JWT auth
The first authenticated-user concept in Aurora Core -- everything else in
this codebase is public, read-only catalog data. Foundation for ratings
and a watchlist (not yet built): POST /auth/register, POST /auth/login,
GET /auth/me.

Security specifics worth knowing:
- Passwords hashed with argon2 (via argon2-cffi), never stored plain or
  logged.
- Login returns an identical 401 for a nonexistent email and a wrong
  password on a real one, including matching response timing
  (burn_time_like_a_password_check) -- otherwise either the status code
  or the timing alone lets a client enumerate registered emails.
- AURORA_SECRET_KEY now actually signs JWTs (previously defined but
  unused anywhere) -- .env.example's existing token_urlsafe(48) guidance
  already produces a long enough key, just flagged its new stakes.
- Email matched case-insensitively via explicit normalization in the
  service layer, not relied on from Pydantic's EmailStr (which does not
  lowercase the local part).

12 new integration tests (register/login/me over real HTTP via the
`client` fixture) plus 10 unit tests for the hashing/JWT logic itself,
following the same-day lesson that only unit tests actually execute
locally without Postgres -- the auth core is tested for real here rather
than only through integration tests that might not run before a deploy.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-06 06:15:39 -05:00

92 lines
2.6 KiB
TOML

[project]
name = "aurora-core"
version = "0.1.0"
description = "Aurora Core — production-grade media metadata, discovery, and intelligence backend for the Aurora ecosystem."
readme = "README.md"
requires-python = ">=3.12"
license = { text = "Proprietary" }
authors = [{ name = "Aurora Dragon Studio" }]
dependencies = [
"fastapi>=0.115.0",
"uvicorn[standard]>=0.30.0",
"sqlalchemy>=2.0.35",
"alembic>=1.13.2",
"asyncpg>=0.29.0",
"psycopg2-binary>=2.9.9",
"pydantic>=2.9.0",
"pydantic-settings>=2.5.0",
"redis>=5.0.8",
"arq>=0.26.0",
"httpx>=0.27.0",
"structlog>=24.4.0",
"orjson>=3.10.0",
"uuid6>=2024.7.10",
"python-dotenv>=1.0.1",
"tenacity>=9.0.0",
"prometheus-client>=0.20.0",
"argon2-cffi>=23.1.0",
"pyjwt>=2.9.0",
"email-validator>=2.2.0",
]
[project.optional-dependencies]
dev = [
"pytest>=8.3.0",
"pytest-asyncio>=0.24.0",
"pytest-cov>=5.0.0",
"ruff>=0.6.9",
"mypy>=1.11.2",
"aiosqlite>=0.20.0",
"faker>=28.4.1",
"types-redis",
]
load = [
"locust>=2.31.0",
]
docs = [
"mkdocs>=1.6.0",
"mkdocs-material>=9.5.0",
]
[build-system]
requires = ["setuptools>=68"]
build-backend = "setuptools.build_meta"
[tool.setuptools.packages.find]
include = ["app*"]
[tool.ruff]
line-length = 100
target-version = "py312"
exclude = ["migrations"]
[tool.ruff.lint]
select = ["E", "F", "I", "UP", "B", "SIM", "N"]
# E501: line length handled by formatter, not worth failing CI over.
# B027: MetadataProvider.aclose() is an intentional optional-override hook
# (concrete no-op default), not an accidentally-unimplemented abstract method.
# B008: FastAPI's `Depends(...)` dependency-injection idiom is a call in an
# argument default by design; this is the standard FastAPI pattern.
ignore = ["E501", "B027", "B008"]
[tool.mypy]
python_version = "3.12"
plugins = ["pydantic.mypy"]
ignore_missing_imports = true
disallow_untyped_defs = false
exclude = ["migrations", "tests"]
[tool.pytest.ini_options]
asyncio_mode = "auto"
# The integration test suite shares one session-scoped asyncpg engine
# (tests/conftest.py:engine) across every test, for speed — schema is
# created once instead of per test. asyncpg connections are bound to the
# event loop they were created in, so every fixture/test must run on that
# same loop; pytest-asyncio's default is a fresh loop per test, which
# breaks that assumption ("another operation is in progress" / "attached
# to a different loop"). Pinning both to "session" fixes it.
asyncio_default_fixture_loop_scope = "session"
asyncio_default_test_loop_scope = "session"
testpaths = ["tests"]