# 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 带来的安全收口价值。
