v1oauth 上下文
已授权应用
GET /v1/oauth/grants 与 DELETE /v1/oauth/grants/:clientId:授权关系的唯一入口。
授权关系由 oauth 在签发令牌时建立,也只有 oauth 知道某个应用当前还挂着几个会话。auth 上下文只认「用户登录过哪些设备」,它的会话记录里没有任何客户端授权信息,因此这份数据不在 auth 侧重复暴露。
端点
| 方法 | 路径 | 说明 | 要求 |
|---|---|---|---|
GET | /v1/oauth/grants | 列出已授权应用 | aud=oauth、scope oauth:grants:read |
DELETE | /v1/oauth/grants/:clientId | 撤销某个应用的授权 | aud=oauth、scope oauth:grants:revoke、max_age=300 |
调用方是管理控制台:它用 RFC 8693 令牌交换,把 auth 会话令牌换成 oauth 受众的短期资源令牌(5 分钟、不落库、受众裁剪为 oauth / account)。
GET /grants
查询参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
limit | number | 可选 | 默认 20,上限 100 |
cursor | string | 可选 | 上一页的游标 |
kind | first_party | third_party | 可选 | 只取某一节 |
响应
{
"success": true,
"grants": [
{
"client_id": "cli_...",
"name": "Example App",
"client_type": "third_party",
"did": null,
"scopes": ["openid", "profile"],
"session_count": 2,
"authorized_at": "2026-10-02T12:00:00.000Z",
"last_issued_at": "2026-11-13T09:21:00.000Z"
}
],
"next_cursor": null
}| 字段 | 说明 |
|---|---|
did | 第一方授权的设备标识;第三方为 null |
user_agent | 该设备最近一次登录的原始 UA;第三方不出现 |
session_count | 该应用当前挂着的会话数 |
scopes | 当前有效的 scope 集合 |
按最近签发时间倒序。第一方客户端按设备聚合,因此同一个应用可能出现在多行——每行的 did 不同,可以单独撤销。
为什么要按 kind 分节取数
两节各自独立分页。若混排成「按最近活跃」的一页再让前端自己拆分,用户会看到某一节空着,而它其实在后面几页。
DELETE /grants/:clientId
参数
| 参数 | 位置 | 必填 | 说明 |
|---|---|---|---|
clientId | 路径 | ✅ | 要撤销的应用 |
did | 查询 | 可选 | 只撤销该设备;缺省撤销整个应用 |
{ "success": true, "message": "Authorization revoked", "revoked": 2 }| 语义 | 说明 |
|---|---|
不带 did | 第三方语义:作废该应用在所有设备上的访问权 |
带 did | 第一方语义:只作废那一台设备上的授权 |
did 是查询参数而不是路径段
它是对同一条关系的收窄条件,不是另一个资源。路径里多一段会让「应用」与「设备」看起来是两级实体。
撤销是「一次性作废」,因此要求 max_age=300;只读的列表端点不要求。撤销不接受 RFC 9396 的授权详情——那是 /authorize 的事。