Files
Web_BAI_Manage_ApiServer/pocket-base/spec/changes.2026-03-23-pocketbase-hooks-auth-hardening.md

176 lines
4.8 KiB
Markdown
Raw Normal View History

# OpenSpec 变更记录PocketBase Hooks 认证链路加固
## 日期
- 2026-03-23
## 范围
本次变更覆盖 `pocket-base/` 下 PocketBase hooks 项目的微信登录、资料更新、token 刷新、认证落库、错误可观测性、索引策略与运行规范。
---
## 一、认证模型调整
### 1. 认证体系
- 保持 PocketBase 原生 auth token 作为唯一正式认证令牌。
- 登录与刷新响应统一通过 `$apis.recordAuthResponse(...)` 返回 PocketBase 原生认证结果。
- `authMethod` 统一使用空字符串 `''`,避免触发不必要的 MFA / login alerts 校验。
### 2. Header 规则
- 正式认证 Header 为:`Authorization: Bearer <token>`
- 非标准 Header `Open-Authorization` 不属于本项目接口定义。
- `users_wx_openid` Header 已从 active hooks 鉴权链路移除。
---
## 二、身份字段与数据模型约束
### 1. openid 作为唯一业务身份锚点
- `tbl_auth_users` 仅保留 `openid` 作为微信身份锚点。
- 业务逻辑中不再使用 `users_wx_openid`
- 用户查询、token 刷新、资料更新均基于 auth record 的 `openid`
### 2. auth 集合兼容字段
由于 `tbl_auth_users` 当前为 PocketBase `auth` 集合,登录注册时为兼容 PocketBase 原生 auth 校验,新增以下兼容策略:
- `email` 使用占位格式:`<openid>@wechat.local`
- 自动生成随机密码
- 自动补齐 `passwordConfirm`
说明:
- 占位 `email` 仅用于满足 auth 集合保存条件,不代表用户真实邮箱。
- 业务主身份仍然是 `openid`
### 3. 自定义字段可空策略
- `tbl_auth_users` 的自定义字段目标约束为:除 `openid` 外,其余业务字段均允许为空。
- 已将 schema 脚本中的 `user_id` 改为非必填。
- 其余业务字段保持非必填。
---
## 三、查询与排序修复
### 1. 移除无意义的 `created` 排序
在 hooks 查询中,以下查询原先使用 `'-created'` 排序:
-`openid` 查询用户
-`company_id` 查询公司
-`users_phone` 查询重复手机号
该写法在 PocketBase 当前运行场景下触发:
- `invalid sort field "created"`
现已统一移除排序参数,改为空排序字符串,因为这些查询本质上均为精确匹配或去重检查,不依赖排序。
---
## 四、错误可观测性增强
### 1. 登录路由显式错误响应
`POST /api/wechat/login` 新增局部 try/catch
- 保留业务状态码
- 返回 `{ code, msg, data }`
- 写入 `logger.error('微信登录失败', ...)`
### 2. 全局错误包装顺序修正
- `routerUse(...)` 全局错误包装提前到路由注册前。
- 统一兼容 `err.statusCode` / `err.status`
### 3. auth 保存失败透传
新增 `saveAuthUserRecord(record)` 包装 `$app.save(record)`
- 失败时统一抛出 `保存微信用户失败`
- 附带 `originalMessage``originalData`
目的:
- 避免 PocketBase 默认 `Something went wrong while processing your request.` 吞掉具体原因。
---
## 五、数据库索引策略修复
### 1. users_phone 索引调整
原设计:
- `users_phone` 唯一索引
问题:
- 新用户注册阶段手机号为空,多个空值会触发唯一约束冲突,导致注册失败。
现调整为:
- `users_phone` 普通索引
说明:
- 手机号唯一性改由业务逻辑在资料完善阶段校验。
- 允许多个未完善资料用户以空手机号存在。
---
## 六、接口契约同步结果
当前 active PocketBase hooks 契约如下:
- `POST /api/system/test-helloworld`
- `POST /api/system/health`
- `POST /api/wechat/login`
- `POST /api/wechat/profile`
- `POST /api/wechat/refresh-token`
其中:
### `POST /api/wechat/login`
- body 必填:`users_wx_code`
- 自动以微信 code 换取 `openid`
- 若不存在 auth 用户则尝试创建 `tbl_auth_users` 记录
- 成功时返回 PocketBase 原生 token + auth record + meta
### `POST /api/wechat/profile`
-`Authorization`
- 基于当前 auth record 的 `openid` 定位用户
- 服务端用 `users_phone_code` 换取手机号后保存
### `POST /api/wechat/refresh-token`
-`Authorization`
- 直接基于当前 auth record 返回新的 PocketBase 原生 token
---
## 七、当前已知边界
1. `tbl_auth_users` 为 PocketBase `auth` 集合,因此仍受 PocketBase auth 内置规则影响。
2. 自定义字段“除 openid 外均可空”已在脚本层按目标放宽,但 auth 集合结构更新仍可能触发 PocketBase 服务端限制。
3. 若线上仍返回 PocketBase 默认 400需要确保最新 hooks 已部署并重启生效。
---
## 八、归档建议
部署时至少同步以下文件:
- `pocket-base/bai-api-main.pb.js`
- `pocket-base/bai_api_pb_hooks/`
- `script/pocketbase.newpb.js`
并在 PocketBase 环境中执行 schema 同步后重启服务,再进行接口验证。