add auth
This commit is contained in:
22 files changed
+1034
-25
No files matched your search
@@ -179,6 +179,8 @@ and you can fill everything in from **Settings**.
|
||||
| `JIRA_*`, `TODOIST_*`, `GOOGLE_*`, `LAUNDRY_*` | seeds for the sources |
|
||||
| `TELEGRAM_BOT_TOKEN`, `TELEGRAM_CHAT_ID` | the bot |
|
||||
| `SCHEDULER_ENABLED`, `TELEGRAM_BOT_ENABLED` | switch the background jobs / bot off |
|
||||
| `AUTH_USERNAME`, `AUTH_PASSWORD` | closes the **browser UI** behind a login; see [Going public](#going-public) |
|
||||
| `TELEGRAM_OWNER_ID`, `TELEGRAM_BOT_USERNAME` | lets that same browser login happen via the Telegram Login Widget |
|
||||
|
||||
---
|
||||
|
||||
@@ -200,7 +202,7 @@ and you can fill everything in from **Settings**.
|
||||
|
||||
* `backend/` — FastAPI app. `app/services/` holds the thinking (timer, tasks,
|
||||
stats, report, blocker policy, scheduler, bot); `app/sources/` holds one module
|
||||
per integration; `app/api/` is a thin HTTP layer. 44 tests under `backend/tests`.
|
||||
per integration; `app/api/` is a thin HTTP layer. 63 tests under `backend/tests`.
|
||||
* `frontend/` — React + TypeScript + MUI, built by Vite into the backend image and
|
||||
served from the same origin (so there is no CORS to configure).
|
||||
* **The look** — warm paper, ink type, hairline rules instead of boxes and
|
||||
@@ -228,14 +230,46 @@ make report # build and send today's report right now
|
||||
The backend also has a CLI, handy from cron or a systemd timer:
|
||||
|
||||
```bash
|
||||
python -m app.cli refresh # pull every source once
|
||||
python -m app.cli briefing # send the morning briefing
|
||||
python -m app.cli report # build and send today's report
|
||||
python -m app.cli stats # print the numbers as JSON
|
||||
python -m app.cli refresh # pull every source once
|
||||
python -m app.cli briefing # send the morning briefing
|
||||
python -m app.cli report # build and send today's report
|
||||
python -m app.cli stats # print the numbers as JSON
|
||||
python -m app.cli set-password # set/rotate the web login password (prompts, never argv)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Going public
|
||||
|
||||
Kadence is still single-user: `AUTH_USERNAME`/`AUTH_PASSWORD` and
|
||||
`TELEGRAM_OWNER_ID` all name *you*, not a user table. What they do is close the
|
||||
**browser UI** to everyone else once the backend is reachable beyond your own
|
||||
machine — `API_TOKEN` already protected the raw API; this is the same idea with
|
||||
a login screen instead of a header the desktop app fills in for you.
|
||||
|
||||
1. **Password.** Set `AUTH_PASSWORD` (and optionally `AUTH_USERNAME`, default
|
||||
`timka`) in `.env` and restart — the hash is seeded into the database on
|
||||
first boot, the plaintext is never stored. A browser now has to sign in at
|
||||
`/`; the desktop app is unaffected, it keeps sending `X-API-Token`. Rotate
|
||||
the password later from **Settings → Access** (while signed in) or by
|
||||
running `make set-password` on the server — the UI never accepts a new
|
||||
password over an unauthenticated connection.
|
||||
2. **Telegram login, in addition** (optional). You already have the bot token
|
||||
(`TELEGRAM_BOT_TOKEN`) for notifications; login reuses it:
|
||||
* DM [@userinfobot](https://t.me/userinfobot) for your numeric Telegram id
|
||||
→ `TELEGRAM_OWNER_ID`.
|
||||
* Tell [@BotFather](https://t.me/BotFather) `/setdomain` and give it this
|
||||
site's **HTTPS** domain — the Login Widget refuses to render anywhere else.
|
||||
* Set `TELEGRAM_BOT_USERNAME` (no `@`) in `.env`, or fill it in under
|
||||
**Settings → Access** once the UI is reachable.
|
||||
* Restart. The login screen grows a "Log in with Telegram" button under the
|
||||
password form.
|
||||
3. Either credential alone is enough to close the UI; set both and either one
|
||||
signs you in. With neither set, the browser stays as open as the bare API
|
||||
always was — nothing changes until you opt in.
|
||||
|
||||
---
|
||||
|
||||
## Security notes
|
||||
|
||||
* The desktop app never runs as root. The only privileged code is
|
||||
@@ -245,6 +279,14 @@ python -m app.cli stats # print the numbers as JSON
|
||||
hostname. It is authorised by polkit (`auth_admin_keep` by default — one prompt
|
||||
per session; `install.sh --passwordless` drops that for one user).
|
||||
* Tokens live in the database and in `.env`, are masked in every API response,
|
||||
and are never sent to the browser in full.
|
||||
* Set `API_TOKEN` before exposing the backend beyond localhost; put it behind TLS
|
||||
if it leaves your network.
|
||||
and are never sent to the browser in full. The password is never stored at
|
||||
all — only a salted PBKDF2 hash.
|
||||
* Set `API_TOKEN` before exposing the backend beyond localhost (protects the
|
||||
raw API — desktop app, scripts, cron); see [Going public](#going-public) to
|
||||
close the **browser UI** the same way, with a login instead of a header.
|
||||
* The login cookie is `HttpOnly`, `SameSite=Lax`, and `Secure` whenever the
|
||||
request arrives as HTTPS (directly or via `X-Forwarded-Proto` from a reverse
|
||||
proxy) — put the backend behind TLS before relying on it over the open
|
||||
internet, same as `API_TOKEN` always needed.
|
||||
* Login attempts are rate-limited per source IP (in-process; fine for one
|
||||
instance, one user).
|
||||
Reference in new issue
Block a user