Magic-link tokens were persisted in plaintext, so a database read disclosed
usable login/invite tokens. The service now hashes tokens (HMAC-SHA256 when a
pepper is configured, else SHA-256 of the high-entropy token) and persists
only the hash; the raw token is exposed solely in the registration URL and is
re-attached to objects returned to callers.
Refs: porchlight-42h
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Use Annotated[str, Form()] for FastAPI dependencies (FAST002)
- Add missing type annotations across src/ and tests/ (ANN001/003/201/202)
- Reduce function arguments via request.form() reads (PLR0913)
- Combine return paths to reduce return statements (PLR0911)
- Use anyio.Path for async-safe filesystem operations (ASYNC240)
- Extract constants, helpers, and dict comprehensions for clarity
- Move inline imports to top-level (PLC0415)
- Use raw strings for regex match patterns (RUF043)
- Fix redundant get_session_user call in delete_user (not-iterable)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>