110 lines
6.6 KiB
Markdown
110 lines
6.6 KiB
Markdown
|
|
# M3 — Token 鉴权与移动端(远期试水)
|
|||
|
|
|
|||
|
|
> 阅读前提:先读 [`README.md`](./README.md)。M3 依赖 M2(已有 `/api` JSON 契约与 session 鉴权)。
|
|||
|
|
>
|
|||
|
|
> **定位**:远期、低投入、探索性。React Native 部分主要是"没做过、试试水"。范围**可能收缩**——其中**token 鉴权 + ingestion 端点收口**是有持久价值的安全改进,应优先;RN app 是加分项。Orchestrator 可只取前半。
|
|||
|
|
|
|||
|
|
## 1. 目标
|
|||
|
|
|
|||
|
|
1. 引入 **bearer token 鉴权**,让非浏览器客户端(移动端、设备脚本)能安全访问。
|
|||
|
|
2. 把 M2 暂时维持裸奔的 **ingestion 端点**(`POST /location/record`、`POST /poo/record`)收口到 token 鉴权下。
|
|||
|
|
3. 做一个 **React Native** 移动端,用类 OAuth 流程拿 token 后消费现有 `/api`。
|
|||
|
|
|
|||
|
|
## 2. 现状(M2 完成后)
|
|||
|
|
|
|||
|
|
- `/api/*` 走 session cookie + `X-CSRF-Token`。
|
|||
|
|
- `app/services/auth.py` 有 server-side session(`auth_sessions` 表,token_hash 存储)。
|
|||
|
|
- `POST /location/record`、`POST /poo/record` 仍**无鉴权**(设备/脚本裸调用)。
|
|||
|
|
|
|||
|
|
## 3. 目标架构
|
|||
|
|
|
|||
|
|
### 3.1 Token 模型
|
|||
|
|
- 新表 `auth_tokens`:`id`、`user_id`、`token_hash`(仅存哈希)、`label`(设备名)、`created_at`、`expires_at`(可空=长期)、`revoked_at`。
|
|||
|
|
- bearer 校验:`Authorization: Bearer <token>` → 哈希比对 `auth_tokens` → 命中且未撤销未过期则认定身份。
|
|||
|
|
|
|||
|
|
### 3.2 类 OAuth 签发流程(无第三方的 Authorization Code 简化版)
|
|||
|
|
1. 移动端在**内置浏览器**打开 `/authorize?...`。
|
|||
|
|
2. 用户账号密码登录(走现有 session),页面展示"授权此设备"。
|
|||
|
|
3. 批准后服务端生成**一次性 authorization code**,重定向到 app 深链 `homeautomation://callback?code=...`。
|
|||
|
|
4. app 用 code 调 `POST /api/auth/token` 换取 bearer token 并存本地。
|
|||
|
|
> 简化兜底:批准页直接展示一次性 token 由 app 捕获。优先实现重定向 + code 交换的正规版。
|
|||
|
|
|
|||
|
|
### 3.3 统一鉴权依赖
|
|||
|
|
- `/api` 的数据/CRUD 端点接受**session cookie 或 bearer**两者之一(同一套端点同时服务 Web 与移动端)。
|
|||
|
|
- ingestion 端点(location/poo record)改为**要求 bearer**。
|
|||
|
|
|
|||
|
|
### 3.4 React Native
|
|||
|
|
- **Expo + TypeScript**,复用 M2 的 OpenAPI 类型化 client(共享契约)。
|
|||
|
|
- 内置浏览器走 §3.2 流程拿 token;之后所有请求带 `Authorization: Bearer`。
|
|||
|
|
|
|||
|
|
## 4. 迁移注意(重要)
|
|||
|
|
- ingestion 端点一旦要求 bearer,**现有调用方(HA/设备脚本)必须先配置 token**,否则记录会中断。
|
|||
|
|
- 上线顺序:先签发 token 能力(T01–T02)→ 给现有设备配 token → 再对 ingestion 端点强制 bearer(T03),避免断流。可设一个过渡开关或灰度。
|
|||
|
|
|
|||
|
|
## 5. 任务依赖图
|
|||
|
|
```
|
|||
|
|
M3-T01 token 模型 + 迁移
|
|||
|
|
└─► M3-T02 签发流程(authorize + code 交换)
|
|||
|
|
└─► M3-T03 统一鉴权依赖 + ingestion 端点收口(含过渡开关)
|
|||
|
|
├─► M3-T04 Web 端 token 管理 UI(列出/撤销设备)
|
|||
|
|
└─► M3-T05 React Native app(试水)
|
|||
|
|
└─► M3-T06 文档收尾
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 6. 原子任务(任务卡)
|
|||
|
|
|
|||
|
|
### M3-T01 — token 数据模型 + Alembic 迁移
|
|||
|
|
- **Status**: `todo` · **Depends**: none(M2 完成后)
|
|||
|
|
- **Files**: `create app/models/token.py`、`alembic_app/versions/<new>_auth_tokens.py`;`modify app/models/__init__.py`;`create tests/test_token_model.py`
|
|||
|
|
- **Acceptance**:
|
|||
|
|
- [ ] 迁移在全新库 upgrade 后建出 `auth_tokens` 表;downgrade 可回滚。
|
|||
|
|
- [ ] token 仅以哈希存储(与 `auth_sessions` 同等强度),明文不入库。
|
|||
|
|
- [ ] 校验闸门全绿。
|
|||
|
|
- **Reviewer**: 哈希算法/长度与现有 session token 一致;`expires_at` 可空语义明确。
|
|||
|
|
|
|||
|
|
### M3-T02 — 签发流程:authorize 页 + code 交换端点
|
|||
|
|
- **Status**: `todo` · **Depends**: M3-T01
|
|||
|
|
- **Files**: `create app/api/routes/api/token.py`、`app/schemas/token.py`;前端 `/authorize` 页(M2 SPA 内);`create tests/test_api_token.py`
|
|||
|
|
- **Acceptance**:
|
|||
|
|
- [ ] 登录用户在 `/authorize` 批准后得到一次性 code;`POST /api/auth/token` 用 code 换取 bearer,code 一次性且短时效。
|
|||
|
|
- [ ] 未登录访问 `/authorize` 跳登录;无效/过期 code 换取失败。
|
|||
|
|
- [ ] 返回的 bearer 仅此一次明文出现,库中只存哈希。
|
|||
|
|
- [ ] 校验闸门全绿。
|
|||
|
|
- **Reviewer**: code 一次性、绑定用户、短 TTL;深链 redirect 白名单校验,防开放重定向。
|
|||
|
|
|
|||
|
|
### M3-T03 — 统一鉴权依赖 + ingestion 端点收口
|
|||
|
|
- **Status**: `todo` · **Depends**: M3-T02
|
|||
|
|
- **Files**: `modify app/dependencies.py`(新增"cookie 或 bearer"统一身份依赖);`modify app/api/routes/location.py`、`poo.py`(要求 bearer,带过渡开关);`modify tests`
|
|||
|
|
- **Acceptance**:
|
|||
|
|
- [ ] `/api` 数据/CRUD 端点用合法 bearer 可访问(等价于 session)。
|
|||
|
|
- [ ] ingestion 端点:带合法 bearer 通过,缺/错 token 在强制模式下 401;过渡开关可临时放行(默认关)。
|
|||
|
|
- [ ] 撤销的 token 立即失效。
|
|||
|
|
- [ ] 校验闸门全绿。
|
|||
|
|
- **Reviewer**: 过渡开关默认安全(强制);bearer 与 session 两路鉴权不产生绕过;ingestion 行为变更有测试覆盖。
|
|||
|
|
|
|||
|
|
### M3-T04 — Web 端 token 管理 UI
|
|||
|
|
- **Status**: `todo` · **Depends**: M3-T03
|
|||
|
|
- **Acceptance**: 在 SPA 内可列出已签发设备 token(label/创建时间/最近使用)、可撤销;撤销后该 token 立即失效;前端闸门全绿。
|
|||
|
|
|
|||
|
|
### M3-T05 — React Native app(试水)
|
|||
|
|
- **Status**: `todo` · **Depends**: M3-T03 · `[experimental]`
|
|||
|
|
- **Files**: `create mobile/`(Expo + TS,复用 OpenAPI 类型化 client)
|
|||
|
|
- **Acceptance**:
|
|||
|
|
- [ ] 内置浏览器走签发流程拿到 token 并安全存储(Keychain/Keystore)。
|
|||
|
|
- [ ] 至少跑通:登录拿 token → 拉取一类数据展示 → 记一条 ingestion。
|
|||
|
|
- [ ] `npm run lint`/`typecheck`/`build`(或 Expo 等价) 全绿。
|
|||
|
|
- **Reviewer**: token 存安全存储而非明文;client 基于共享 OpenAPI 类型。
|
|||
|
|
|
|||
|
|
### M3-T06 — 文档收尾
|
|||
|
|
- **Status**: `todo` · **Depends**: M3-T05
|
|||
|
|
- **Acceptance**: README/architecture 增 token 鉴权与移动端说明;roadmap 勾选 M3;`openapi/` 同步。
|
|||
|
|
|
|||
|
|
## 7. 里程碑完成定义(DoD)
|
|||
|
|
- 存在 bearer token 鉴权与签发流程;token 仅哈希存储、可撤销。
|
|||
|
|
- ingestion 端点已收口到 bearer(过渡完成后强制)。
|
|||
|
|
- `/api` 同时支持 session 与 bearer。
|
|||
|
|
- (加分)React Native app 能拿 token 并消费 `/api`。
|
|||
|
|
- 后端 + 前端 + 移动端各自校验闸门全绿,`openapi/` 入库。
|
|||
|
|
|
|||
|
|
> 提醒:本里程碑探索性强,T05 可作为独立试水随时叫停,不影响 T01–T04 带来的安全收口价值。
|