Files
meida/docs/开发任务包/T10-华为登录代码先行.md
T
xsl e511b6a66f 收尾两批并行开发的文档回写与任务包
- Sprint 执行计划 v1.1:§1.1 补第一批交付实据;T9 SPIKE 结论回写 Sprint 1 表
  (T-P0-16 通过 2.8ms;T-P0-12 有条件;T-P0-10 原型 6/6 + 真实照片重验前置);
  Sprint 2 表标注 T-P2-01~06 交付;新增 §9 两批交付记录
- 开发任务拆解 v1.5:交付回写 + 新风险登记(RTMPose 域差异、BiRefNet 异步化)
- 新增 开发任务包/:三批多 agent 并行的协作规程与 14 份任务契约
- 新增 前置开通清单-云资源与资质.md
- 清理已用毕的评审报告(文档评审 2026-08-13、项目整体评审 2026-08-14),
  README 中相应引用改为留痕表述
2026-08-14 21:07:20 +08:00

87 lines
5.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# T10 · 登录·华为账号(代码先行,联调后置)
> 对应任务:`T-P1-12` | 需求:`FR-ACC-01`(登录成功率 ≥99%、UnionID 映射唯一、首次自动建用户)| 分支:`task/t10-auth-huawei` | worktree:`/home/xsl/meida_backend-t10`
> 先读《README.md(协作规程)》,遵守全部纪律。
## 1. 目标
实现 `POST /auth/huawei`:客户端传华为 Account Kit 签发的 **id_token**,服务端按 OIDC 标准做 **JWKS 验签 + claims 校验**,映射到内部用户并签发双令牌(access + refresh,复用 T4 已交付的 `issue_session`)。
**本任务性质是"代码先行"**:华为 AGC 后台尚未配置(见《前置开通清单》§3),因此真实联调后置;全部逻辑用**自造 RSA 密钥对 + 本地 JWKS** 做单测/集成,凭证到位后只改 `.env` 即可上线。
## 2. 已就绪的契约(协调提交已提供)
### 配置(config.py 已加)
```python
huawei_jwks_url: str = "https://oauth-login.cloud.huawei.com/oauth2/v3/certs" # HUAWEI_JWKS_URL
huawei_audience: str = "" # HUAWEI_AUDIENCE,= AGC App ID,联调时填
huawei_issuer: str = "https://accounts.huawei.com" # HUAWEI_ISSUER(以 Account Kit 文档为准,可配)
huawei_jwks_cache_seconds: int = 86400
```
### 可复用的现有设施(先读再写)
- `packages/identity/service.py`:`get_or_create_by_external_identity(provider, union_id, open_id, register_source)`(T4 已有);`issue_session(user_id) -> SessionIssued`(T4 交付,含 refresh 轮换结构)。
- `packages/shared/security.py`:`create_access_token(...)`(HS256 access)。
- `packages/identity/schemas.py`:`TokenPairResponse`(T4 交付)——**直接复用,不要在本任务重复定义**。
- `pyjwt[crypto]` 已装(含 cryptography,RS256 验签可用)。
### 端点归属
`apps/api/routers/auth.py` 归你(T4 已合并完,本批无他人碰它)。`apps/api/routers/auth_sms.py` 归 T11,**禁碰**。
## 3. 实现要点
允许改动:`packages/identity/huawei.py`(新建,核心逻辑全在此)、`apps/api/routers/auth.py`(追加端点)、测试文件。**不改 identity/service.py 与 identity/schemas.py**(避免与 T11 冲突——你的模型定义在 huawei.py 内)。
1. **`packages/identity/huawei.py`**:
- `HuaweiIdTokenVerifier`:构造参数 `(jwks_url, audience, issuer, cache_seconds, *, http_fetch=None, now=None)`——`http_fetch` 是 `Callable[[str], Awaitable[str]]`(单测注入本地 JWKS,**不连外网**);JWKS 内存缓存(TTL 按配置,kid 变化时强制刷新一次并重试验签);
- `verify(id_token) -> HuaweiClaims`(frozen dataclass:`union_id/open_id/exp/iat`):RS256 验签(`jwt.decode` + `PyJWK`/手工 kid 匹配)、`aud`(等于配置 audience,空配置时**拒绝**而不是跳过——联调前后都必须严格)、`iss`、`exp/iat`、`union_id` 缺失拒绝(华为子账号场景无 union_id 时降级用 open_id 的策略**不做**,注释说明留给联调期决定);
- 错误映射:验签失败/claims 不符 → `AppError(TOKEN_INVALID)`(40102,已有);JWKS 拉取失败 → `AppError(UPSTREAM_ERROR)`(60000,已有)——**不需要新错误码**。
2. **路由**(auth.py 追加):
```python
@router.post("/huawei", summary="华为账号授权登录(FR-ACC-01)")
async def huawei_login(payload: HuaweiLoginRequest, ...) -> ...:
claims = await verifier.verify(payload.id_token)
user = await identity.get_or_create_by_external_identity(
provider="huawei", union_id=claims.union_id, open_id=claims.open_id,
register_source="huawei")
issued = await identity.issue_session(user.user_id)
access = create_access_token(...)
return ok(TokenPairResponse(...)) # 复用 T4 模型
```
- verifier 用依赖注入(路由模块内建一个 `get_huawei_verifier` 的 FastAPI Depends,从 settings 构造并缓存到 `app.state` 或模块级 lru——**不改 deps.py**,在 auth.py 内实现);
- `HuaweiLoginRequest(id_token: str)` 定义在 huawei.py。
3. **模块 docstring** 写明:真实联调前置 = AGC 应用创建 + 证书指纹双套 + `HUAWEI_AUDIENCE` 填 App ID(引用《前置开通清单》§3)。
## 4. 测试要求
- 单测 `tests/unit/test_huawei_verifier.py`:测试内用 `cryptography` 生成 RSA 密钥对、自签 id_token + 本地 JWKS dict:
1. 合法 token → claims 解析正确;
2. 错误签名(换密钥)→ 40102;过期 → 40101(复用 TOKEN_EXPIRED);aud 不符 → 40102;audience 未配置 → 40102;
3. JWKS 拉取失败 → 60000;kid 轮换 → 缓存刷新后验签成功。
- 集成 `tests/integration/test_auth_huawei.py`(新文件):monkeypatch 注入 FakeFetcher(本地 JWKS)→ `POST /v1/auth/huawei` → 200 双令牌;再次登录同 union_id → 同一 user_id(映射唯一);新 access 可调 `GET /v1/quota`。
## 5. 禁改清单
通用禁改之外:`packages/identity/{service,repo,schemas,tokens}.py`、`apps/api/routers/auth_sms.py`、`packages/{kb,rules,retrieval}/**`。
## 6. 验收标准(FR-ACC-01)
1. 验签逻辑完整(签名/aud/iss/exp/iat 全查),audience 未配置时拒绝启动校验路径;
2. UnionID→内部 user_id 映射唯一,首次登录自动建用户(集成断言同 ID 复登);
3. 全链路不连外网(FakeFetcher 注入);
4. 全部检查绿(mypy 范围同 T6 列表)。
## 7. 提交信息
```
feat(identity): Huawei Account Kit login via OIDC JWKS verification
POST /auth/huawei:JWKS 验签(kid 缓存/轮换)+ claims 严格校验 +
UnionID 映射 + T4 双令牌签发(FR-ACC-01;真实联调待 AGC 配置)。
偏差说明:<如有写在这里,无则写"无">
```