# 飞书登录与续期

Version: a2bc6a0f021ec643

这是隔离测试沙箱，全部为虚构数据。下方飞书协议流程使用模拟身份提供者，不会访问真实飞书：让用户在模拟授权页面输入专用沙箱口令并选择测试身份；不要输入真实凭证或扫码。本环境签发 sandbox- 测试凭证，仅用于本沙箱；真实招聘、发送、通知和模型调用已禁用。

Agent 不需要应用密钥或平台自建授权码。用户在飞书页面登录/扫码并确认后即完成鉴权，不存在第二次平台批准。用户必须是本平台所属飞书租户中已启用的成员。

第一步：发起连接。以下默认只请求四类读取权限；任务确需写入时再增加对应的 :write scope。写权限不会自动包含读取权限，需要两者时要一并申请。agent_name 填可识别的名称，不代表可信身份。

```bash
curl 'https://sandbox.maimai.dev.planahr.top/api/v1/agent-auth/device' \
  -H 'Content-Type: application/json' \
  --data '{
  "agent_name": "我的招聘助手",
  "scope": "candidates:read jobs:read clients:read hiring:read"
}'
```

| 返回字段 | 用法 |
| --- | --- |
| verification_uri_complete | 把此飞书完整验证链接展示给用户，由用户打开登录/扫码。 |
| verification_uri / user_code | 备用飞书验证地址和飞书验证码；不是平台生成的授权码。 |
| device_code | 保存在 Agent 端，仅用于 token 轮询；不得展示或泄露。 |
| interval / expires_in | 轮询间隔（秒）和此次连接的有效期（秒）；以实际返回值为准。 |
| business_scope | 本次申请的平台业务权限上限，不会扩大用户本身权限。 |

第二步：让用户完成飞书验证，然后按返回 interval 轮询。把下例 <DEVICE_CODE> 替换为上一步保存的值，不要拼到 URL 中。

```bash
curl 'https://sandbox.maimai.dev.planahr.top/api/v1/agent-auth/token' \
  -H 'Content-Type: application/json' \
  --data '{
  "grant_type": "urn:ietf:params:oauth:grant-type:device_code",
  "device_code": "<DEVICE_CODE>"
}'
```

| OAuth error | 处理 |
| --- | --- |
| authorization_pending | 等待至少 interval 秒继续，不是登录失败。 |
| slow_down | 在当前轮询间隔上增加至少 5 秒。 |
| access_denied / expired_token / invalid_grant | 停止本次流程，告知用户；如仍需要连接，重新发起登录。 |
| temporarily_unavailable / 429 | 有限退避，并遵守 Retry-After；不要高频轮询。 |

成功后保存 access_token、可选的 refresh_token 及它们的有效期。token_type 为 Bearer；scope 是飞书返回的 scope，business_scope 才是平台业务权限上限。可选 client_id=planahr-agent 只是本服务的公开客户端配置名，不是飞书 app_id，也不是密钥。

续期同样由飞书签发凭证。使用最近一次返回的 refresh_token；刷新需要串行，保存返回的最新 token/有效期（飞书可能轮换，也可能保留 refresh_token）。没有可用续期凭证时重新登录。

把下例 <REFRESH_TOKEN> 替换为最近保存的续期凭证。调用业务接口时，下例环境变量 ACCESS_TOKEN 则替换为当前 access_token。

```bash
curl 'https://sandbox.maimai.dev.planahr.top/api/v1/agent-auth/token' \
  -H 'Content-Type: application/json' \
  --data '{
  "grant_type": "refresh_token",
  "refresh_token": "<REFRESH_TOKEN>"
}'
```

用户可在平台的“Agent 授权”页面查看和撤销自己的连接。平台撤销不等于撤销整个飞书应用授权，也不会让网页登录退出。
