# Security Notes This document summarizes the current security-relevant behavior of MiauInv. It is intended as implementation documentation, not as a guarantee that the application is production-ready for untrusted public deployments. ## Authentication MiauInv uses signed JWT access tokens, database-backed refresh tokens, and optional TOTP-based two-factor authentication. JWTs are signed with `JWT_SECRET`. They are not encrypted. Normal access tokens and purpose tokens should therefore contain only identity and authorization metadata. The short-lived 2FA setup token is a narrow exception: it carries the not-yet-enabled TOTP secret until the first authenticator code is validated. This avoids storing the setup secret in the database before 2FA is confirmed. ## Passwords Passwords are hashed with bcrypt. Password updates require the current password. New passwords longer than bcrypt's effective 72-byte limit are rejected. When a password is changed: 1. The new password is bcrypt-hashed. 2. The stored password hash is updated. 3. Existing refresh tokens for the user are revoked. 4. A new session is issued for the current browser. ## Sessions Access tokens expire after 15 minutes. Refresh tokens expire after 7 days. Refresh tokens are stored only as hashes in the database. Refresh-token rotation revokes the used refresh token and inserts a new token hash. Security-sensitive account changes revoke existing refresh-token sessions. ## Cookies Authentication cookies are set as HTTP-only secure cookies using `SameSite=Lax`. Because the cookies are marked `Secure`, local development should use HTTPS. If the application is placed behind a reverse proxy, the deployment should preserve HTTPS semantics between the user and the proxy. ## Two-Factor Authentication TOTP 2FA is optional per account. The setup flow returns a QR code, a manual setup key, and a short-lived setup token. The TOTP secret is stored only after the user submits a valid code from their authenticator app. When 2FA is enabled: 1. Any previous recovery codes are deleted. 2. A new recovery-code set is generated. 3. The TOTP secret is stored. 4. Existing refresh sessions are revoked. 5. A new current session is issued. When 2FA is disabled: 1. The TOTP secret is cleared. 2. Recovery codes are deleted. 3. Existing refresh sessions are revoked. 4. Authentication cookies are cleared. ## Recovery Codes Recovery codes are generated with cryptographically secure randomness and stored only as hashes. They are displayed only immediately after generation or regeneration. They cannot be recovered later because the plaintext values are not stored. Recovery codes are single-use. During login, a submitted value is first checked as a TOTP code. If that fails, the value is normalized, hashed, and matched against unused recovery-code hashes. The account settings UI warns the user when the remaining unused recovery-code count is low. ## Rate Limiting Basic in-memory rate limiting protects login, 2FA, refresh, registration, and sensitive account endpoints. This is suitable for a single-instance private deployment. It is not sufficient for multi-instance deployments because limiter state is process-local. A public or multi-instance deployment should use persistent or distributed rate limiting at the application, reverse proxy, or infrastructure layer. ## Known Limitations - Automated testing is currently limited. - TOTP secrets are stored in the database after confirmation because the server must validate future codes. - TOTP secrets are not encrypted at rest. - There is no dedicated session/device management UI yet. - There is no audit log for account security changes yet. - The current rate limiter is process-local and memory-only. - Passkeys/WebAuthn are intentionally not implemented yet.