# 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).