Skip to content

Add API key auth, secure CORS defaults, and a Postgres-backed integration suite - #6

Merged
j0shcap merged 6 commits into
mainfrom
security-hardening
Aug 20, 2026
Merged

Add API key auth, secure CORS defaults, and a Postgres-backed integration suite#6
j0shcap merged 6 commits into
mainfrom
security-hardening

Conversation

@j0shcap

@j0shcap j0shcap commented Aug 20, 2026

Copy link
Copy Markdown
Owner

Summary

Authentication

  • Protected endpoints (POST /push/send, GET /devices/all, DELETE /devices) require Authorization: Bearer <API_KEY>, validated by a FastAPI dependency with constant-time comparison; missing/invalid credentials get 401 with WWW-Authenticate: Bearer
  • POST /devices/register stays open by design: it is called by the iOS app, and shipping the key in the app binary would expose it — worst case is junk registrations, which APNs pruning removes
  • The server fails fast at startup when API_KEY is unset

CORS

  • The wildcard-with-credentials middleware is gone. CORS is disabled unless CORS_ORIGINS lists explicit origins; main.py gained a create_app() factory so this is testable

Endpoint and config hygiene

  • GET /devices/clear (an unauthenticated destructive GET) is now DELETE /devices with auth; a regression test pins the old route as gone
  • SQL echo is off by default and opt-in via DB_ECHO (statements include device tokens)
  • Strict boolean env parsing shared between DB_ECHO and APNS_USE_SANDBOX

Integration test suite

  • tests/integration/ boots the real uvicorn process against real Postgres and drives it over HTTP: startup schema creation, the full device lifecycle, every auth path, empty-recipient push through the authed stack, and graceful shutdown (asserted exit code 0)
  • CI now runs them on every push/PR via a Postgres service container; locally they skip with instructions unless INTEGRATION_DB_HOST is set
  • scripts/e2e_apns_sandbox.py checked in as the documented manual check against Apple's real sandbox (needs credentials, so not CI-able)

Test plan

  • 75 tests pass: 66 unit/API (TDD — every new behavior had a failing test first) + 9 integration against Postgres 16
  • ruff, ruff format --check, and mypy clean
  • Live end-to-end verified: real server + real Postgres + real APNs sandbox — 401 without/with wrong key, per-token typed APNs result with the correct key, keyless registration, authed delete, refusal to boot without API_KEY, clean shutdown

@j0shcap
j0shcap merged commit 057abf7 into main Aug 20, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant