M4-T09: document login hardening, finalize OpenAPI and roadmap
This commit is contained in:
@@ -69,12 +69,30 @@ React SPA 前端(M2 引入)。Vite + React + TypeScript + Mantine,由 Fast
|
||||
|
||||
### `scripts/`
|
||||
|
||||
辅助脚本目录。当前包含 OpenAPI 导出脚本(`export_openapi.py`)与数据层辅助脚本。
|
||||
辅助脚本目录。当前包含:
|
||||
|
||||
- `export_openapi.py`:导出 OpenAPI schema 静态产物
|
||||
- `run_migrations.py`:运行 Alembic migration
|
||||
- `app_db_adopt.py`:App DB 接管 / 初始化
|
||||
- `migrate_legacy_data.py`:一次性历史数据搬迁脚本
|
||||
- `admin_cli.py`:Admin CLI 逃生通道(M4),见下方"登录加固"说明
|
||||
|
||||
### `openapi/`
|
||||
|
||||
OpenAPI schema 静态产物(`openapi.json` / `openapi.yaml`),由 `python scripts/export_openapi.py` 生成,纳入版本控制。前端 codegen 以此为契约源。
|
||||
|
||||
## 登录加固(M4)
|
||||
|
||||
M4 在基础 Argon2 + server-side session 鉴权之上叠加了三层防御:
|
||||
|
||||
**防爆破 / 指数退避**:`app/services/login_throttle.py` 按 client IP 与 username 双键记失败计数,失败超过 3 次后指数增长等待时间(最长 15 分钟),`POST /api/auth/login` 在退避窗口内直接返回 `429 + Retry-After`,不执行 Argon2 验证。成功登录后清零。退避是延迟而非永久封号;全局开关 `AUTH_LOGIN_THROTTLE_ENABLED`(CONFIG_FIELDS,默认开);反代后需设 `AUTH_TRUST_FORWARDED_FOR=true`(`.env` 部署级,默认 false)。
|
||||
|
||||
**CLI 逃生通道**:`scripts/admin_cli.py`(入口 `python -m scripts.admin_cli`)直连本地 DB,**无需 HTTP 服务运行、无需任何已存凭据**,支持:重置密码(`reset-password`)、解锁退避(`unlock`)、关停 TOTP(`disable-totp`,零凭据最终逃生)、重新发放 TOTP secret(`reissue-totp`)、查看用户列表(`list-admin`)。CLI 只动 auth 行,不触碰用户数据表。
|
||||
|
||||
**可选 TOTP 二次验证**:admin 可在设置页自选启用 RFC 6238 TOTP。启用后登录为两步(密码 → 6 位动态码或一次性恢复码);不启用维持纯密码。TOTP secret 明文存库(与其他 secret 一致,靠文件权限保护);恢复码以 Argon2 哈希存储,使用后消费(一次性)。后端使用 `pyotp`,二维码由前端 `qrcode.react` 渲染。issuer 标签由 `AUTH_TOTP_ISSUER`(`.env` 部署级)配置,默认回退 `app_name`。
|
||||
|
||||
详细说明:[`docs/auth.md`](./auth.md)
|
||||
|
||||
## 当前约束
|
||||
|
||||
- 当前数据库继续使用 SQLite
|
||||
|
||||
+191
-82
@@ -1,120 +1,229 @@
|
||||
# 基础鉴权说明
|
||||
# 鉴权说明
|
||||
|
||||
本文档说明当前 Python 重构项目里已经落地的第一版鉴权基座。
|
||||
|
||||
这一轮只解决:
|
||||
|
||||
- 登录页
|
||||
- 登录 / 登出流程
|
||||
- server-side session
|
||||
- 一个最小受保护页面
|
||||
|
||||
这一轮明确不解决:
|
||||
|
||||
- 完整 config persistence
|
||||
- 完整 config CRUD
|
||||
- 多用户权限系统
|
||||
- OAuth / SSO / RBAC
|
||||
本文档说明当前已落地的鉴权基座(基础 session 鉴权 + M4 登录加固)。
|
||||
|
||||
## 当前 auth 模型
|
||||
|
||||
- 认证方式:`username/password`
|
||||
- 认证方式:`username/password`(可选启用 TOTP 二次验证)
|
||||
- 会话方式:server-side session
|
||||
- 客户端凭据:session cookie
|
||||
- 页面形态:Jinja server-side template
|
||||
|
||||
## 当前持久化
|
||||
## 持久化
|
||||
|
||||
当前新增一个共享 App DB:
|
||||
所有 auth 相关数据存放在单一 App DB(`APP_DATABASE_URL`,默认 `sqlite:///./data/app.db`)中:
|
||||
|
||||
- `APP_DATABASE_URL`
|
||||
- 默认值:`sqlite:///./data/app.db`
|
||||
|
||||
当前 auth 相关数据存放在这个 DB 中:
|
||||
|
||||
- `auth_users`
|
||||
- `auth_sessions`
|
||||
- `app_config`
|
||||
|
||||
当前没有把 auth 数据和 `location` / `poo` DB 混放。
|
||||
|
||||
当前这部分现在也走 Alembic 管理:
|
||||
|
||||
- Alembic 环境:`alembic_app.ini` + `alembic_app/`
|
||||
- 初始化脚本:`python scripts/app_db_adopt.py`
|
||||
|
||||
当前没有 legacy app DB,所以这一版脚本只负责初始化新库,不负责 legacy adoption。
|
||||
|
||||
`app_config` 现在承接运行时配置持久化。
|
||||
|
||||
其中:
|
||||
|
||||
- `.env` 负责 bootstrap / fallback
|
||||
- `app_config` 表负责运行时配置覆盖
|
||||
- 登录密码仍然属于认证数据,使用 Argon2 哈希,不存进 `app_config`
|
||||
- `auth_users`:用户表(含 TOTP 字段 `totp_secret` / `totp_enabled`)
|
||||
- `auth_sessions`:session token 哈希与过期时间
|
||||
- `auth_login_throttle`:登录失败退避状态(按 IP / username 双键)
|
||||
- `auth_recovery_code`:TOTP 恢复码哈希(一次性)
|
||||
- `app_config`:runtime 配置持久化
|
||||
|
||||
## 首次启动与 bootstrap
|
||||
|
||||
如果 auth DB 中还没有任何用户,应用启动时会要求:
|
||||
如果 auth DB 中还没有任何用户,应用启动时会使用:
|
||||
|
||||
- `AUTH_BOOTSTRAP_USERNAME`
|
||||
- `AUTH_BOOTSTRAP_PASSWORD`
|
||||
|
||||
并创建首个 admin 用户。
|
||||
|
||||
当前默认 bootstrap 值就是:
|
||||
创建首个 admin 用户。当前默认 bootstrap 值为:
|
||||
|
||||
- username: `admin`
|
||||
- password: `admin`
|
||||
|
||||
首次登录后,系统会强制要求修改密码。
|
||||
|
||||
如果你希望在首次启动前就覆盖默认值,可以直接设置环境变量:
|
||||
## 基础安全设计
|
||||
|
||||
- `AUTH_BOOTSTRAP_USERNAME`
|
||||
- `AUTH_BOOTSTRAP_PASSWORD`
|
||||
|
||||
建议流程是:
|
||||
|
||||
1. 配好 `.env`
|
||||
2. 运行 `python scripts/app_db_adopt.py`
|
||||
3. 启动应用
|
||||
4. 用 `admin / admin` 首次登录
|
||||
5. 立即修改密码
|
||||
|
||||
## 安全设计
|
||||
|
||||
当前这版已经落实的基础安全点:
|
||||
当前这版已经落实的安全点:
|
||||
|
||||
- 密码不明文存储,使用 Argon2 哈希
|
||||
- session cookie 为 `HttpOnly`
|
||||
- cookie 使用 `SameSite=Lax`
|
||||
- `Secure` cookie 在非 `development` 环境默认开启
|
||||
- 登录表单与登出表单都有基础 CSRF 校验
|
||||
- 写请求(POST/PUT/PATCH/DELETE)需携带 `X-CSRF-Token` header(SameSite=Lax + 自定义 header 纵深防御,无需 per-session token 值比对)
|
||||
- session token 为随机生成,服务端只持久化 token hash
|
||||
- session 有过期时间与显式失效机制
|
||||
- session 有过期时间(默认 12 小时)与显式失效机制
|
||||
|
||||
## 当前受保护范围
|
||||
---
|
||||
|
||||
当前这轮只保护了页面入口:
|
||||
## M4 登录加固
|
||||
|
||||
- `GET /config`
|
||||
- `POST /config`
|
||||
- `POST /config/change-password`
|
||||
- `POST /logout`
|
||||
M4 在基础鉴权之上叠加了三层防御:防爆破/指数退避、CLI 逃生通道、可选 TOTP 二次验证。
|
||||
|
||||
相关流程:
|
||||
### 1. 防爆破 / 指数退避
|
||||
|
||||
- `GET /login`
|
||||
- `POST /login`
|
||||
#### 机制
|
||||
|
||||
未登录访问 `/config` 时会被重定向到 `/login`。
|
||||
登录失败按指数增长延迟,目标是拖垮暴力枚举,同时不因此永久锁定账号。
|
||||
|
||||
## 下一步不在本轮内
|
||||
**退避是延迟(429 + Retry-After),不是永久封号**——单 admin 场景下永久锁会被攻击者反向用来故意打锁,所以退避只增加等待时间,CLI 是最终逃生口。
|
||||
|
||||
后续可以在这个基座上继续做:
|
||||
#### 双键计算
|
||||
|
||||
- 配置页面接入
|
||||
- config persistence
|
||||
- 更细的受保护路由范围
|
||||
- 用户初始化 / 密码轮换的更正式 runbook
|
||||
每次登录请求同时按 **client IP** 和 **username** 各记一套失败计数,本次需等待时间 = 两者退避的**较大值**。
|
||||
|
||||
- 按 IP:堵单点暴力(攻击 IP 越敲越慢,合法用户换 IP 不受影响)
|
||||
- 按 username:全局兜底(即便攻击者分布式换 IP 也有一层保护)
|
||||
|
||||
#### 指数公式
|
||||
|
||||
```
|
||||
N_FREE = 3 — 前 3 次失败不延迟(免费次数)
|
||||
BASE = 1 秒
|
||||
CAP = 900 秒(15 分钟)
|
||||
|
||||
wait = min(CAP, BASE × 2^(failures - N_FREE)) (failures > N_FREE 时生效)
|
||||
```
|
||||
|
||||
延迟示意:
|
||||
|
||||
| 累计失败次数 | 等待时间 |
|
||||
| --- | --- |
|
||||
| 1–3 | 0 秒(免费) |
|
||||
| 4 | 2 秒 |
|
||||
| 5 | 4 秒 |
|
||||
| 6 | 8 秒 |
|
||||
| 7 | 16 秒 |
|
||||
| … | … |
|
||||
| 13+ | 900 秒(封顶) |
|
||||
|
||||
成功登录后,该 IP 和 username 的退避状态**立即清零**。
|
||||
|
||||
#### 登录端点行为(`POST /api/auth/login`)
|
||||
|
||||
1. 先查退避(在窗口内直接 `429`,**不验密码**,节省 Argon2 计算并防止枚举)
|
||||
2. 验密码失败 → 记一次失败(IP + username 各记),返回 `401`
|
||||
3. 密码正确但 TOTP 启用且缺少 `totp_code` → 返回 `401 {totp_required: true}`,**不记失败**(这是正常两步流程的第一步,不是攻击信号)
|
||||
4. 密码正确但 TOTP 验证失败 → 记一次失败,返回 `401`
|
||||
5. 全部通过 → 清零退避状态,发 session cookie
|
||||
|
||||
#### 配置项
|
||||
|
||||
| 配置项 | 类型 | 默认 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `AUTH_LOGIN_THROTTLE_ENABLED` | bool(CONFIG_FIELDS) | `true` | 全局开关,关闭后整个退避机制 no-op |
|
||||
| `AUTH_TRUST_FORWARDED_FOR` | bool(`.env` 部署级) | `false` | `true` 时取 `X-Forwarded-For` 最左作为 client IP;**反代后必须显式开启**,否则反代 IP 全视为同一 IP,按 IP 退避失效 |
|
||||
|
||||
> **反代部署注意**:`AUTH_TRUST_FORWARDED_FOR` 默认 `false`(直接用 socket IP)。部署在 nginx 等反代后面时,应设 `AUTH_TRUST_FORWARDED_FOR=true`,并确保反代正确设置 `X-Forwarded-For`。
|
||||
|
||||
---
|
||||
|
||||
### 2. CLI 逃生通道
|
||||
|
||||
#### 设计原则
|
||||
|
||||
CLI 通过 **直连本地 DB**(`get_session_local()`)工作,**无需 HTTP 服务器运行、无需任何已存凭据**(密码、恢复码均不需要)。拿到服务器 CLI 权限本身就意味着对系统有完全控制,因此这是可接受且必须存在的最终逃生口。
|
||||
|
||||
CLI 只动 auth 相关行(`auth_users` 的密码/TOTP 字段、`auth_login_throttle`、`auth_recovery_code`),**绝不**触碰用户数据表(`location`、`poo_records`、`public_ip_state` 等)。
|
||||
|
||||
入口:`python -m scripts.admin_cli <command>`
|
||||
|
||||
#### 子命令
|
||||
|
||||
| 命令 | 用途 | 逃生场景 |
|
||||
| --- | --- | --- |
|
||||
| `reset-password <username> [--password <pwd>]` | 重置密码(不带 `--password` 则交互式输入,不回显) | 忘记密码 |
|
||||
| `unlock [--all \| --ip <ip> \| --username <u>]` | 清 `auth_login_throttle` 行 | 被退避锁住(429)时解锁 |
|
||||
| `disable-totp <username>` | 关停 TOTP(清 secret + 删全部恢复码),**零凭据可执行** | 恢复码全丢也能进 |
|
||||
| `reissue-totp <username>` | 生成新 TOTP secret 并打印 `otpauth://` URI | 设备丢失、需重新注册 Authenticator |
|
||||
| `list-admin` | 列出所有用户与状态列 | 排障用 |
|
||||
|
||||
#### 使用示例
|
||||
|
||||
```bash
|
||||
# 重置密码(不带 --password 时交互式 prompt,不回显)
|
||||
python -m scripts.admin_cli reset-password admin
|
||||
|
||||
# 重置密码(非交互,脚本里用)
|
||||
python -m scripts.admin_cli reset-password admin --password "newpassword"
|
||||
|
||||
# 解锁所有退避
|
||||
python -m scripts.admin_cli unlock --all
|
||||
|
||||
# 解锁特定 IP
|
||||
python -m scripts.admin_cli unlock --ip 1.2.3.4
|
||||
|
||||
# 解锁特定 username
|
||||
python -m scripts.admin_cli unlock --username admin
|
||||
|
||||
# 关停 TOTP(零凭据,最终逃生口)
|
||||
python -m scripts.admin_cli disable-totp admin
|
||||
|
||||
# 重新发放 TOTP secret(打印新 otpauth:// URI,扫码重新注册)
|
||||
python -m scripts.admin_cli reissue-totp admin
|
||||
|
||||
# 查看用户列表
|
||||
python -m scripts.admin_cli list-admin
|
||||
```
|
||||
|
||||
在 Docker 容器内执行:
|
||||
|
||||
```bash
|
||||
docker compose exec app python -m scripts.admin_cli <command>
|
||||
```
|
||||
|
||||
#### `reissue-totp` 语义说明
|
||||
|
||||
- 对**已启用** TOTP 的用户:新 secret **立即在登录时生效**,旧 Authenticator 生成的码立即失效;**无需**再走 web `enable` 步骤。
|
||||
- 现有恢复码**不被删除**——恢复码是独立的随机哈希值,与 TOTP secret 无密码学绑定,reissue 后恢复码依然有效(仍可用于登录)。
|
||||
- 如果需要完整清理(secret + 所有恢复码),使用 `disable-totp`。
|
||||
|
||||
---
|
||||
|
||||
### 3. 可选 TOTP 二次验证
|
||||
|
||||
#### 设计
|
||||
|
||||
- TOTP 遵循 RFC 6238,使用 `pyotp` 库。
|
||||
- 二维码在**前端**由 `qrcode.react` 渲染(后端只返回 `otpauth://` URI,不引图像依赖)。
|
||||
- `totp_secret` 明文存库(与项目其他 secret 处理一致,靠数据库文件权限保护)。
|
||||
- 恢复码以 Argon2 哈希存库,一次性(使用后标记 `used_at`)。
|
||||
|
||||
#### 启用流程
|
||||
|
||||
1. **setup**(`POST /api/auth/totp/setup`):生成 pending secret,返回 secret、`otpauth://` URI、10 个明文恢复码(**仅此一次**);此时 `totp_enabled` 仍为 `false`。
|
||||
2. **扫码**:用 Authenticator App 扫前端渲染的二维码(或手动输入 secret)。
|
||||
3. **enable**(`POST /api/auth/totp/enable`):输入当前 6 位码确认 → `totp_enabled` 变为 `true`。
|
||||
4. 妥善保存恢复码(不会再次展示)。
|
||||
|
||||
#### 停用流程
|
||||
|
||||
**web 停用**(`POST /api/auth/totp/disable`):需提供当前密码或当前 6 位 TOTP 码。成功后清 secret、删全部恢复码,恢复纯密码登录。
|
||||
|
||||
**CLI 停用**(逃生口,恢复码全丢时):`python -m scripts.admin_cli disable-totp admin`,零凭据,立即生效。
|
||||
|
||||
#### 登录二步流程
|
||||
|
||||
1. 提交 `POST /api/auth/login {username, password}` → 密码正确但 TOTP 已启用 → `401 {totp_required: true}`(不发 session)
|
||||
2. 前端切到第二屏,提交 `POST /api/auth/login {username, password, totp_code}` → 通过 → 发 session cookie
|
||||
|
||||
`totp_code` 可以是 6 位 TOTP 动态码,也可以是 `xxxx-xxxx` 格式恢复码(命中即消费,不可复用)。
|
||||
|
||||
#### API 端点
|
||||
|
||||
| 端点 | 用途 |
|
||||
| --- | --- |
|
||||
| `POST /api/auth/totp/setup` | 生成 pending secret + URI + 恢复码(一次性明文返回) |
|
||||
| `POST /api/auth/totp/enable` | 带当前 6 位码确认启用 |
|
||||
| `POST /api/auth/totp/disable` | 带密码或当前码停用 |
|
||||
| `GET /api/auth/totp` | 返回当前 TOTP 状态(`{enabled: bool}`),不返回 secret/恢复码 |
|
||||
|
||||
全部端点需要 session cookie(`GET /api/auth/totp` 不需 CSRF;其余写端点需 `X-CSRF-Token`)。
|
||||
|
||||
#### 配置项
|
||||
|
||||
| 配置项 | 类型 | 默认 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `AUTH_TOTP_ISSUER` | str(`.env` 部署级) | 空(回退 `app_name`) | 显示在 Authenticator App 里的 issuer 标签 |
|
||||
|
||||
---
|
||||
|
||||
## 受保护范围
|
||||
|
||||
当前 JSON API 端点(`/api/*`)需要 session cookie;写端点需额外携带 `X-CSRF-Token` header。
|
||||
|
||||
裸 ingestion 端点(`/location/record`、`/poo/record` 等设备调用端点)暂未收口到 session 保护(M3 计划引入 token 鉴权)。
|
||||
|
||||
## 下一步(不在当前范围)
|
||||
|
||||
- M3:token 鉴权(供脚本 / 设备 / 移动端调用 API),ingestion 端点收口。
|
||||
|
||||
@@ -247,7 +247,7 @@ Phase B(TOTP,可选配)
|
||||
- **Reviewer checklist**: 全走类型化 client;secret/恢复码不落 localStorage/日志;空/错/加载态有处理。
|
||||
|
||||
### M4-T09 — 文档 + OpenAPI + roadmap 收尾
|
||||
- **Status**: `todo` · **Depends**: 全部
|
||||
- **Status**: `done` · **Depends**: 全部
|
||||
- **Files**: `modify README.md`、`docs/auth.md`(防爆破 + CLI + TOTP 说明、CLI 用法)、`docs/architecture-overview.md`;`modify docs/roadmap.md`(把"TOTP 二次验证"与新"登录防爆破"从「下一阶段」毕业、加 M4 行、注明 M4 先于 M5);`modify docs/design/README.md`(已含 m4);`run python scripts/export_openapi.py` 提交 `openapi/`
|
||||
- **Acceptance criteria**:
|
||||
- [ ] 文档含 CLI 逃生用法与 TOTP 启停说明;`git diff --exit-code openapi/` 无差异。
|
||||
|
||||
+24
-30
@@ -2,7 +2,7 @@
|
||||
|
||||
本文档记录 `home-automation` 在 `v1.0.3` 之后的下一阶段规划。这一阶段不是小修补,而是几次较大的结构性改动:单库化、前端重写、以及远期的移动端试水。
|
||||
|
||||
> 每个里程碑的**可执行原子任务**展开在 [`docs/design/`](./design/README.md):M1 [`m1-db-consolidation.md`](./design/m1-db-consolidation.md)、M2 [`m2-frontend-v2.md`](./design/m2-frontend-v2.md)、M3 [`m3-token-mobile.md`](./design/m3-token-mobile.md)。这些文档为 Orchestrator→Implementer→Reviewer 的多模型流水线设计。
|
||||
> 每个里程碑的**可执行原子任务**展开在 [`docs/design/`](./design/README.md):M1 [`m1-db-consolidation.md`](./design/m1-db-consolidation.md)、M2 [`m2-frontend-v2.md`](./design/m2-frontend-v2.md)、M3 [`m3-token-mobile.md`](./design/m3-token-mobile.md)、M4 [`m4-login-hardening.md`](./design/m4-login-hardening.md)。这些文档为 Orchestrator→Implementer→Reviewer 的多模型流水线设计。
|
||||
|
||||
## 当前基线(v1.0.3)
|
||||
|
||||
@@ -36,9 +36,11 @@
|
||||
| --- | --- | --- |
|
||||
| **M1** ✅ | 单库化地基 | 把三库合并成单一 `app.db`,清理散落数据层,删掉 Grafana |
|
||||
| **M2** ✅ | 前端 v2 | React SPA 取代 Jinja,承载 config + 可视化 + 记录增删改 |
|
||||
| **M4** ✅ | 登录加固 | 防爆破/指数退避 + CLI 逃生通道 + 可选 TOTP 二次验证(**先于 M5**) |
|
||||
| **M5** | IoT / 能耗采集 | Modbus/Energy + MQTT/HA Discovery + 前端侧边栏 |
|
||||
| **M3** | 开放与移动端(远期试水) | token 鉴权 + React Native 移动端 |
|
||||
|
||||
排序原则:**先清地基,再在干净结构上盖楼。** M2 的新 API 和 React 必须建立在合并后的单库之上,否则就是在准备推倒的旧数据层上盖新楼、之后回头返工。
|
||||
排序原则:**先清地基,再在干净结构上盖楼。** M2 的新 API 和 React 必须建立在合并后的单库之上;M4 是公网安全加固,在 M5 IoT 集成之前先堵住裸密码这个洞;M5 在安全基座就绪后再做 IoT 接入。
|
||||
|
||||
---
|
||||
|
||||
@@ -132,6 +134,22 @@
|
||||
|
||||
---
|
||||
|
||||
## M4 — 登录加固(✅ 已完成,排在 M5 之前)
|
||||
|
||||
### 目标
|
||||
|
||||
给暴露在公网的单 admin 登录做纵深防御,先于 M5 IoT 集成关闭暴力枚举和单因子风险。
|
||||
|
||||
### 范围
|
||||
|
||||
- **防爆破 / 指数退避**:失败登录按双键(IP + username)指数增长延迟,成功即清零。退避是延迟(429 + Retry-After),不是永久封号。全局开关 `AUTH_LOGIN_THROTTLE_ENABLED`(CONFIG_FIELDS);反代后需开 `AUTH_TRUST_FORWARDED_FOR`(`.env` 部署级)。
|
||||
- **CLI 逃生通道**:`python -m scripts.admin_cli` 直连本地 DB,无需 HTTP 服务、无需任何已存凭据;支持重置密码、解锁退避、关停/重发 TOTP、查看用户。
|
||||
- **可选 TOTP 二次验证**:admin 可自选启用;启用后两步登录(密码 + 6 位动态码或一次性恢复码);不启用维持纯密码。CLI `disable-totp` 是连恢复码都丢了时的最终逃生口。
|
||||
|
||||
> 详细设计与任务卡:[`docs/design/m4-login-hardening.md`](./design/m4-login-hardening.md)
|
||||
|
||||
---
|
||||
|
||||
## M3 — 开放与移动端(远期试水)
|
||||
|
||||
### 目标
|
||||
@@ -151,39 +169,15 @@
|
||||
|
||||
## 下一阶段:已确定要做(尚未拆解为任务卡)
|
||||
|
||||
> 这些是 M2 之后**已经定下来要做**的方向——区别于下面的 Future Ideas(仅备忘、未必做)。这里只记到 roadmap 粒度:确定**做什么、为什么**;具体排期、依赖与原子任务,等动手时再展开成 `docs/design/` 的任务卡。**先后顺序未定**,部分项(如 MQTT)时间点灵活,可提前也可靠后。
|
||||
> 这些是 M4 之后**已经定下来要做**的方向——区别于下面的 Future Ideas(仅备忘、未必做)。这里只记到 roadmap 粒度:确定**做什么、为什么**;具体排期、依赖与原子任务,等动手时再展开成 `docs/design/` 的任务卡。**先后顺序未定**,具体排期等动手时再定。
|
||||
|
||||
### 1. TOTP 二次验证(Dashboard 加固)
|
||||
|
||||
**动机**:M2 之后多了一个 Web Dashboard。它虽有单 admin 密码保护,但**大概率会暴露在公网**上,只靠密码这一层不够。给登录再叠一层 **TOTP(基于时间的一次性密码,RFC 6238)** 作为第二因子,做纵深防御。
|
||||
|
||||
**范围(粗略,待细化)**:
|
||||
|
||||
- 在现有单 admin(Argon2 + server-side session)登录之上,叠加 TOTP 第二步:密码校验通过后再验 6 位动态码,通过才发 session cookie。
|
||||
- 首次启用时生成 TOTP secret,给出可导入 Authenticator 的二维码 / 可手输密钥;同时生成一组一次性**恢复码(recovery codes)**。
|
||||
|
||||
**运维 / 命令行要求(关键,实现时必须满足)**:
|
||||
|
||||
1. **忘记密码**:不需要任何 Web 端“找回密码”流程——直接在命令行里重置 admin 密码即可(沿用现有 CLI 思路)。
|
||||
2. **TOTP 重置 / 恢复**:必须提供**命令行重置入口**。要覆盖最坏情况——**连恢复码(restore key)都丢了**,也能纯靠 CLI 把 TOTP 关掉 / 重新发放新的 secret,从而恢复登录。即:**CLI 是不依赖任何已存恢复凭据的最终逃生通道**,不能出现“密钥丢了就彻底锁死”的死角。
|
||||
|
||||
### 2. 前端优化
|
||||
### 1. 前端优化
|
||||
|
||||
**动机**:M2 的 React SPA 先把功能跑通,性能 / 体验层面的打磨还没做。这一项**确定要做,但具体优化什么还没定**。
|
||||
|
||||
**范围(待定)**:方向先留空,想清楚再细化。可能的候选(仅占位、非承诺):打包体积与代码分割(M2 构建已提示存在 > 500 kB 的单 chunk)、首屏加载、热力图 / 地图的渲染性能、移动端适配、可访问性等。等确定具体目标后再拆任务卡。
|
||||
|
||||
### 3. MQTT 与 IoT 集成
|
||||
|
||||
**动机**:把这个后端接入家里的 IoT 设备生态,用 **MQTT** 作为设备 ↔ 后端的消息通道。属于**确定的实现方向**,时间点灵活——可以放到后面,也可以提前先做一部分。
|
||||
|
||||
**范围(粗略,待细化)**:
|
||||
|
||||
- 引入 MQTT(接入既有 broker 或自带一个),后端作为订阅 / 发布方与设备互通。
|
||||
- 与现有模块(Home Assistant in/out、location / poo recorder 等)如何衔接、哪些数据走 MQTT,待细化。
|
||||
- 设备侧鉴权 / 安全边界另议(可能与下面第 4 条的 token 共用一套凭据)。
|
||||
|
||||
### 4. 设置页生成 Long-lived Token(供 API 调用)
|
||||
### 2. 设置页生成 Long-lived Token(供 API 调用)
|
||||
|
||||
**动机**:浏览器端走 session cookie 即可,但**脚本 / 设备 / 外部程序调用 API** 需要一种长期有效、可随身携带的凭据。在设置页加一组功能,由 admin **手动签发 long-lived token**,之后用它来调 API。
|
||||
|
||||
@@ -197,4 +191,4 @@
|
||||
|
||||
> 这里收集**还没排进里程碑、也还没决定要不要做**的想法。不是承诺、也没有先后顺序;想做时再从这里捞出来——先升进上面的「下一阶段」,再细化成 `docs/design/` 的任务卡。
|
||||
|
||||
_(暂无条目:原 TOTP 已确定要做,已上移到「下一阶段」。)_
|
||||
_(暂无条目。)_
|
||||
|
||||
Reference in New Issue
Block a user