--- title: Planner Auth — Real Authentication emoji: 🔐 colorFrom: indigo colorTo: green sdk: docker app_port: 7860 pinned: false license: mit --- # Planner Auth 🔐 A **real (server-side) authentication server** **+ protected planner**: passwords hashed with bcrypt, sessions managed by the server with an `httpOnly` cookie, each user's data stored and filtered **on the server**, and an `/app` page that only opens with a valid session. ## How to use it 1. Open the Space → **Sign up** → create an account → click **Open the Planner**. 2. The planner (`/app`) loads habits, tasks and focus sessions **from the server API** — nothing in IndexedDB. 3. Try opening `/app` while logged out: the server redirects you to the login. 4. Reload the page: you're still logged in (the session lives on the server). ## Endpoints ### Authentication | Route | What it does | |---|---| | `POST /api/register` | validates, hashes with bcrypt (10 rounds), creates the user, starts a session | | `POST /api/login` | bcrypt compare, **regenerates the session** (anti fixation), rate limit of 5 tries / 15 min | | `POST /api/logout` | destroys the session on the server | | `GET /api/me` | protected — returns the session's user, or 401 | | `POST /api/forgot-password` | random token, stored only as a SHA-256 hash, 15 min validity | | `POST /api/reset-password` | validates the token (single use) and replaces the password hash | ### Per-user data — all require a session and filter by `owner` | Route | What it does | |---|---| | `GET/POST /api/habits`, `PUT/DELETE /api/habits/:id` | the logged-in user's habits (emoji, color, daily/weekly with a goal) | | `GET /api/checkins`, `POST /api/checkins/toggle` | daily completions (`{habitId, date}`), validates habit ownership | | `GET/POST /api/tasks`, `PUT/DELETE /api/tasks/:id`, `POST /api/tasks/reorder` | tasks with priority and due date, reordering by id list | | `GET/POST /api/sessions` | focus (pomodoro) sessions | | `GET /app` | the planner page — **redirects to `/` without a session** | ## Security details - **bcrypt** with 10 rounds — the password is never stored as plain text. - **Server-side session**: the `planner.sid` cookie is `httpOnly`, `sameSite=lax`, automatically `Secure` behind the Space's HTTPS; secret generated with `crypto.randomBytes` and persisted. - **Session regeneration on login** (anti fixation) and login rate limiting. - **`/app` outside `express.static`**: the file lives in `private/` and is only served by the route that requires a session — there is no way to download it directly. - **Per-user isolation on the server**: every data route filters by `owner = current session`; a user cannot read or change another user's data, even by forging ids. - **Reset token** stored as a SHA-256 hash, single use, expiring; the "forgot password" response is identical whether the e-mail exists or not. ## Honest limitations - Storage is `data/db.json` (swap for SQLite/Postgres in production). - Sessions are in memory (`MemoryStore`) — they don't survive a Space restart; use Redis in production. - No e-mail in a Space: the reset token comes back in the API response (demo). In production it would go by e-mail. - No explicit CSRF token (`sameSite=lax` covers the basic case); no HTTPS of its own (the Space proxy handles it).