From bdb798efddedcf42b914503718121757171bfff2 Mon Sep 17 00:00:00 2001 From: "n.siegfried" Date: Mon, 29 Jun 2026 14:52:27 -0700 Subject: [PATCH] Add Portainer deploy guide for the login portal Standalone instructions for the Portainer admin: set AUTH_SECRET_KEY, rebuild + redeploy the stack, and bootstrap the first admin account. Co-Authored-By: Claude Opus 4.8 (1M context) --- DEPLOY-login-portal.md | 103 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 DEPLOY-login-portal.md diff --git a/DEPLOY-login-portal.md b/DEPLOY-login-portal.md new file mode 100644 index 0000000..51e2663 --- /dev/null +++ b/DEPLOY-login-portal.md @@ -0,0 +1,103 @@ +# Deploy: Work Package Suite — login portal update + +Instructions for the **Portainer admin** to take the new secure login portal live. +No prior context needed. + +**Repo:** `Project-SDE-WP-Suite` (primegit) — changes are merged to **`main`**. + +**What changed:** the app now has a username/password login. Going live needs: +1. one new environment variable, +2. a **rebuild** of the stack (not just a restart), and +3. creating the first admin account. + +> **Why a rebuild (not a restart):** both the **nginx/webserver** and **api** images +> bake the code in at build time (`COPY html/` and `COPY server/` in their +> Dockerfiles). A plain restart will **not** pick up the new code — the images must +> be **rebuilt** from the latest `main`. + +--- + +## 1. Add an environment variable to the stack + +In the stack's **Environment variables** section, add: + +| Name | Value | Notes | +|------|-------|-------| +| `AUTH_SECRET_KEY` | a long random string | **Required.** Signs the login session cookies. | +| `AUTH_SESSION_HOURS` | `12` | *Optional.* Hours a login lasts before re-auth (defaults to 12). | + +Generate the secret on the host with: + +```bash +openssl rand -base64 48 +``` + +> If `AUTH_SECRET_KEY` is **not** set, the app still starts but falls back to a random +> per-process key — logins then reset on every restart and break across the 2 gunicorn +> workers. It must be set to a fixed value. + +The existing database variables (`POSTGRES_*`) are unchanged. + +--- + +## 2. Pull latest `main`, rebuild, and redeploy + +- Pull the latest commit on `main` and redeploy the stack **with image rebuild enabled** + (e.g. "Re-pull and redeploy" / force rebuild). This rebuilds both the `webserver` and + `api` images. +- New Python dependencies (`bcrypt`, `PyJWT`) are in `requirements.txt` and install + automatically during the rebuild. +- The `users` table is created automatically on API startup — **no DB migration needed.** + +--- + +## 3. Verify the containers + +- Confirm `wp_api` and the webserver container are both **running**. +- If `wp_api` fails to start, check its **Logs**. (A missing `AUTH_SECRET_KEY` only logs a + warning — it won't crash — but please confirm it's set.) + +--- + +## 4. Create the first admin account + +The login system needs one admin user in the production (Postgres) database. Open the +**`wp_api`** container's **Console** (`/bin/sh`) and run: + +```bash +python -m server.manage_users create-admin --name "" +``` + +It prompts for a password (minimum 8 characters) and prints `Created admin: `. + +Non-interactive alternative: + +```bash +python -m server.manage_users create-admin --name "" --password "" +``` + +Other CLI commands (run the same way): `list`, `create --role user`, +`reset-password `, `disable `, `enable `. + +--- + +## 5. Confirm it works + +1. Load the site's normal URL — it should redirect to a **login page**. +2. Sign in with the admin account from step 4. +3. That admin can then add all other users from the in-app **Admin → User + administration** page (top-right **Admin** link), so no further shell access is needed. + +--- + +## Reference — what's in this release + +- `server/auth.py` — bcrypt password hashing, JWT session cookie, the request gate. +- `server/app.py` — `/api/auth/*` endpoints + middleware that refuses every `/api` data + route without a valid session. +- `server/manage_users.py` — the CLI used in step 4. +- `html/login.html`, `html/auth-guard.js` — login page and per-page guard. +- `html/admin.html` / `admin.js` — Admin Console gated on the admin role, with the user + administration UI. +- Sessions are stateless: a signed JWT in an **HttpOnly, SameSite=Lax** cookie, marked + **Secure** automatically when served over HTTPS (via `X-Forwarded-Proto` from nginx).