217 lines
6.5 KiB
Markdown
217 lines
6.5 KiB
Markdown
# Logto 受保护 API Mock 联调设计
|
||
|
||
## 背景
|
||
|
||
当前扩展已经具备基础的 Logto 登录能力:
|
||
|
||
- popup 可触发登录和登出
|
||
- background 可通过 `@logto/chrome-extension` 获取 access token
|
||
- 页面功能在未登录时会被 auth gate 拦住
|
||
|
||
但现阶段业务数据仍然来自页面站内接口,尚未真正验证“扩展拿到 Logto access token 后,请求受保护 API”这条链路。后续真实后端将由其他人提供,因此当前阶段的目标不是接入正式接口,而是先在本地完成一套可重复验证的模拟联调方案。
|
||
|
||
## 目标
|
||
|
||
- 新增一个专用于 Logto 受保护 API 的扩展客户端。
|
||
- 扩展请求受保护 API 前,必须先通过 background 获取 access token。
|
||
- 请求时自动附加 `Authorization: Bearer <token>` 请求头。
|
||
- 提供一个本地 mock 受保护 API,用于验证扩展到后端的完整调用链路。
|
||
- 提供自动化测试,分别覆盖:
|
||
- token 注入逻辑
|
||
- mock API 授权成功/失败行为
|
||
|
||
## 非目标
|
||
|
||
- 不接入真实业务后端。
|
||
- 不做真实 JWT 签名校验、JWKS 拉取或资源权限判定。
|
||
- 不修改现有星图页面数据采集逻辑为正式后端模式。
|
||
- 不在本阶段设计复杂的 token 刷新监控或重试策略。
|
||
|
||
## 方案对比
|
||
|
||
### 方案 A:只做本地 mock 后端
|
||
|
||
- 直接搭一个假接口,扩展请求它并观察返回。
|
||
- 优点:最接近最终使用方式。
|
||
- 缺点:如果联调失败,不容易快速判断问题出在 token 注入还是 mock 服务本身。
|
||
|
||
### 方案 B:只做代码级测试
|
||
|
||
- 不起本地服务,只用测试替身验证请求头是否带 token。
|
||
- 优点:实现快,定位问题直接。
|
||
- 缺点:无法证明完整链路可运行。
|
||
|
||
### 方案 C:先代码级测试,再接本地 mock 后端
|
||
|
||
- 先用单元测试锁定 token 注入行为,再起 mock 服务完成真实联调。
|
||
- 优点:定位问题更快,同时保留完整链路验证。
|
||
- 缺点:改动稍多于单一方案。
|
||
|
||
推荐采用方案 C。
|
||
|
||
## 架构设计
|
||
|
||
### 1. 扩展受保护 API 客户端
|
||
|
||
新增一个独立客户端模块,职责如下:
|
||
|
||
- 向 background 发送 `auth:get-access-token` 消息
|
||
- 读取返回的 access token
|
||
- 使用该 token 请求指定后端地址
|
||
- 将接口成功、未授权、网络失败这三类结果转成明确错误
|
||
|
||
这个客户端不直接耦合星图 DOM,也不依赖 popup。它只负责“拿 token 并带 token 发请求”,这样后续替换正式后端时只需要调整接口地址和返回映射。
|
||
|
||
### 2. Background 认证桥接
|
||
|
||
现有 background 已支持 `auth:get-access-token`。本次不改变登录主流程,只把它当作唯一 token 来源:
|
||
|
||
- content script 不直接接触 Logto SDK
|
||
- 所有受保护 API 请求都通过 background 提供 token
|
||
|
||
这样可以保持认证逻辑集中,符合 MV3 扩展的边界约束。
|
||
|
||
### 3. 本地 mock 受保护 API
|
||
|
||
新增一个轻量本地服务作为测试后端,建议职责保持极小:
|
||
|
||
- 暴露固定测试 endpoint,例如 `/api/mock/protected`
|
||
- 检查请求头中是否存在 `Authorization`
|
||
- 如果请求头形如 `Bearer <非空字符串>`,返回固定假数据
|
||
- 如果没有该头,返回 `401`
|
||
|
||
本阶段不要求验证 token 是否来自真实 Logto,只验证“扩展是否按约定附带了 Bearer token”。
|
||
|
||
## 数据流
|
||
|
||
完整调用链路如下:
|
||
|
||
1. 扩展中的业务入口调用受保护 API 客户端
|
||
2. 客户端向 background 发送 `auth:get-access-token`
|
||
3. background 返回当前 access token
|
||
4. 客户端带上 `Authorization: Bearer <token>` 请求本地 mock API
|
||
5. mock API 校验请求头并返回假数据
|
||
6. 客户端将结果回传给调用方
|
||
|
||
失败分支:
|
||
|
||
- 如果 background 没返回合法 token,客户端直接报错,不发请求
|
||
- 如果 mock API 返回 `401`,客户端将其识别为未授权错误
|
||
- 如果请求超时或网络失败,客户端抛出网络错误
|
||
|
||
## 接口设计
|
||
|
||
### 扩展内客户端接口
|
||
|
||
建议客户端提供单一入口,例如:
|
||
|
||
- `loadProtectedMockData()`
|
||
|
||
内部行为:
|
||
|
||
- 先取 token
|
||
- 再发 GET 请求
|
||
- 返回结构化响应对象或抛出明确错误
|
||
|
||
后续替换真实后端时,可以保留同样入口,内部再切换到真实 endpoint。
|
||
|
||
### Mock API 返回
|
||
|
||
成功时返回固定 JSON,例如:
|
||
|
||
```json
|
||
{
|
||
"ok": true,
|
||
"source": "mock-protected-api",
|
||
"message": "authorized",
|
||
"receivedAuthHeader": "Bearer ..."
|
||
}
|
||
```
|
||
|
||
失败时返回:
|
||
|
||
```json
|
||
{
|
||
"ok": false,
|
||
"error": "unauthorized"
|
||
}
|
||
```
|
||
|
||
## 错误处理
|
||
|
||
### 无 token
|
||
|
||
- 判定条件:background 未返回 `auth:token`,或 token 为空字符串
|
||
- 处理方式:立即抛错,提示当前未登录或 token 不可用
|
||
|
||
### 未授权
|
||
|
||
- 判定条件:后端返回 `401` 或 `403`
|
||
- 处理方式:抛出授权失败错误,供上层展示“请重新登录”
|
||
|
||
### 网络失败
|
||
|
||
- 判定条件:fetch 抛异常、连接失败、超时
|
||
- 处理方式:抛出网络错误,不吞掉异常原因
|
||
|
||
## 实现边界
|
||
|
||
建议新增或调整如下模块:
|
||
|
||
### `src/content/market` 下新增受保护 API 客户端
|
||
|
||
- 不替换现有页面接口客户端
|
||
- 独立处理 mock 受保护 API 访问
|
||
- 便于后续把“页面抓取模式”和“后端接口模式”并行保留
|
||
|
||
### `src/shared/auth-messages.ts`
|
||
|
||
- 复用现有 `auth:get-access-token`
|
||
- 若现有消息结构足够,则不新增消息类型
|
||
|
||
### `scripts/` 或独立目录中的 mock 服务
|
||
|
||
- 提供本地测试服务启动脚本
|
||
- 默认监听本地固定端口
|
||
- 返回固定 JSON 结果
|
||
|
||
## 测试策略
|
||
|
||
### 单元测试
|
||
|
||
- 客户端请求前会先获取 access token
|
||
- 拿到 token 后,请求头中包含 `Authorization: Bearer <token>`
|
||
- token 缺失时不会发起 fetch
|
||
- 接口返回 `401` 时抛出未授权错误
|
||
- 接口返回成功时正确解析 JSON
|
||
|
||
### 联调测试
|
||
|
||
- 启动本地 mock 服务后,带 token 请求能成功
|
||
- 不带 token 请求返回 `401`
|
||
- 扩展客户端能读到 mock 返回的假数据
|
||
|
||
## 手动验证
|
||
|
||
1. 构建并加载扩展
|
||
2. 在 popup 中完成 Logto 登录
|
||
3. 启动本地 mock API
|
||
4. 触发扩展中的受保护接口请求
|
||
5. 确认 mock API 收到 `Authorization: Bearer <token>`
|
||
6. 确认扩展端收到成功响应
|
||
|
||
## 迁移到真实后端的路径
|
||
|
||
当真实后端可用时,仅需要替换以下内容:
|
||
|
||
- mock API 基地址
|
||
- 具体 endpoint 路径
|
||
- 返回数据结构映射
|
||
- 若真实后端要求额外 scope,则补充 `auth-config` 中的 scopes
|
||
|
||
核心认证链路保持不变:
|
||
|
||
- 仍由 background 提供 token
|
||
- 仍由独立客户端附带 `Bearer` 请求头
|
||
- 仍按未授权和网络错误分别处理
|