文档中心 / 开发者

API 参考

第三方授权 v3 的 REST 端点:client 登记、授权、令牌、签名调用、请求生命周期、撤销与运营端停用。

基础约定

项约定
服务地址https://password.tminos.com
格式请求与响应均为 JSON(Content-Type: application/json),UTF-8
时间UTC,ISO 8601,精确到秒,例如 2026-10-23T08:30:00Z
二进制值base64url,无填充
IDclient_id 以 pmc_ 开头;grant_id、request_id 为 UUID
错误{ "error": "<code>" },见错误码
请求体上限400,000 字节

使用 Swift SDK 时无需直接调用这些端点。本页面向需要自行实现协议或排查问题的开发者。

认证方式

方式用于凭据
账号会话登记 client、同意授权、设备端与用户端管理Authorization: Bearer <会话令牌>,由 SRP 登录获得,30 分钟有效
client 认证POST /api/oauth/token、POST /api/oauth/revokepublic:PKCE verifier(换 token)或审批钥签名(撤销);confidential:client_secret
签名调用全部第三方数据端点Authorization: Bearer <access_token> 加审批钥签名,见签名

签名

除换 token 外,所有第三方数据调用都必须同时携带 access token 与该授权审批私钥的签名。public client 的撤销请求也需要签名。

请求头值
AuthorizationBearer <access_token>
x-pm-timestamp当前 Unix 时间(秒)
x-pm-nonce8–128 个字符的随机串,同一授权内不得重复
x-pm-signature对规范串的 Ed25519 签名,base64url

规范串(UTF-8,各段以 \n 连接,末尾无换行):

{METHOD}\n{PATH}\n{TIMESTAMP}\n{NONCE}\n{SHA256_HEX(BODY)}
  • METHOD:大写的 HTTP 方法。
  • PATH:不含查询串的路径,例如 /api/oauth/requests/3f6b…/patch。
  • BODY:实际发送的请求体字节。签名后不得再改动请求体(包括重新序列化)。无请求体时取空串,其 SHA-256 为 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855。
  • 时间戳与服务器时间相差不得超过 ±300 秒。
  • nonce 在同一授权内 10 分钟内只能使用一次;即使签名校验失败,已提交的 nonce 也视为已使用。
GET /api/oauth/account HTTP/1.1
Host: password.tminos.com
Authorization: Bearer 3q2-7wEXAMPLEACCESSTOKEN
x-pm-timestamp: 1761177600
x-pm-nonce: n-0123456789abcdef
x-pm-signature: KngAgL1nChfn4NugEnlNlesKA8EtmrhpsL1GU9pU_Z0AcwkQRzP9iasdaFfe3nEVcvCGUT92eWHzzZCEX849DQ

上例签名可用测试向量中的公开测试密钥复现。

签名失败的错误码:signature_required(缺请求头)、signature_invalid(格式或签名不对)、timestamp_out_of_window、nonce_replayed。

登记 client

POST /api/oauth/clients

登记一个接入应用。认证:账号会话。MimiKey for Mac 的「开发者接入」页面调用的就是这个端点。

字段类型必填说明
namestring是1–100 个字符
descriptionstring否用途说明
client_typestring否public 或 confidential,缺省 confidential
platformsstring[]否最多 10 个,每个 1–32 个小写字母、数字、_ 或 -;仅用于展示
redirect_urisstring[]是1–10 个,规则见回调地址规则
brandobject否color(#RRGGBB)、logo_url(https,不超过 500 字符)、homepage;其他键忽略
seal_public_keystring否仅 confidential:32 字节 X25519 公钥。public client 的 seal 公钥在每次授权时上送
service_public_keynull否已停用,非空时返回 400 mode_retired
POST /api/oauth/clients HTTP/1.1
Authorization: Bearer <会话令牌>
Content-Type: application/json

{
  "name": "我的 AI 助手",
  "description": "帮我登录网站完成对账",
  "client_type": "public",
  "platforms": ["ios", "macos"],
  "redirect_uris": ["com.example.ai://mimikey/callback"],
  "brand": { "color": "#1d4ed8", "logo_url": "https://cdn.example.com/logo.png", "homepage": "https://example.com" }
}
{
  "client_id": "pmc_Xk3vQ9pL2mN8rT4w",
  "client_type": "public",
  "name": "我的 AI 助手",
  "platforms": ["ios", "macos"],
  "brand": { "color": "#1D4ED8", "logo_url": "https://cdn.example.com/logo.png", "homepage": "https://example.com" }
}

confidential client 的响应另含 client_secret,只返回这一次;服务器只保存其哈希。

错误:invalid_input、invalid_client_type、invalid_redirect_uris、invalid_public_key、invalid_platforms、invalid_brand、mode_retired。

GET /api/oauth/clients

列出当前账号登记的 client。认证:账号会话。

{
  "clients": [
    {
      "client_id": "pmc_Xk3vQ9pL2mN8rT4w",
      "name": "我的 AI 助手",
      "description": "帮我登录网站完成对账",
      "client_type": "public",
      "redirect_uris": ["com.example.ai://mimikey/callback"],
      "platforms": ["ios", "macos"],
      "brand": { "color": "#1D4ED8" },
      "has_seal_key": false,
      "has_service_key": false,
      "disabled": false,
      "platform_disabled": false,
      "active_grants": 3,
      "created_at": "2026-10-23T08:00:00Z"
    }
  ]
}

disabled 表示开发者已停用,platform_disabled 表示被平台运营方停用。

DELETE /api/oauth/clients/{id}

停用一个 client。认证:账号会话。该 client 的全部授权立即撤销、令牌失效、未完成请求过期,不可恢复。响应 { "ok": true };不存在或已停用时返回 404 client_not_found。

授权(MimiKey App 侧)

以下端点由 MimiKey App 用用户的账号会话调用,接入方不需要实现,列出供理解协议。

深链

SDK 打开以下深链启动 MimiKey:

用途URL
授权mimikey://authorize?client_id=…&redirect_uri=…&state=…&code_challenge=…&code_challenge_method=S256&approval_key=…&seal_key=…
处理请求mimikey://request?id=…&client_id=…&redirect_uri=…&state=…
参数说明
client_id登记得到的 ID
redirect_uri登记过的回调地址,逐字匹配
state至少 16 字节的随机串;回调中原样返回,不符时 SDK 必须丢弃回调
code_challengebase64url(SHA-256(code_verifier)),43–128 个字符
approval_key本次授权的审批公钥(Ed25519,32 字节)
seal_key本次授权的 seal 公钥(X25519,32 字节)
id请求 ID

回调参数见回调状态汇总。

GET /api/oauth/authorize/info

同意卡所需的数据。查询参数:client_id。

{
  "client": {
    "id": "pmc_Xk3vQ9pL2mN8rT4w",
    "name": "我的 AI 助手",
    "description": "帮我登录网站完成对账",
    "client_type": "public",
    "brand": { "color": "#1D4ED8", "logo_url": "https://cdn.example.com/logo.png", "homepage": "https://example.com" },
    "platforms": ["ios", "macos"]
  },
  "vaults": [
    { "vault_id": "8d4c…", "name": null, "kind": "personal", "default": true },
    { "vault_id": "b2a1…", "name": "家庭", "kind": "shared", "default": false }
  ]
}

只返回该用户拥有的库。为兼容 v2,client 中还包含 client_id、seal_public_key、service_public_key。错误:404 client_not_found、403 client_disabled。

POST /api/oauth/authorize

记录用户的同意并签发授权码。

字段必填说明
client_id是
redirect_uri是必须是登记值之一
state否原样返回
approval_key_public是32 字节 Ed25519 公钥
seal_public_keypublic 必填32 字节 X25519 公钥;confidential 缺省时使用登记时的 seal 公钥
code_challengepublic 必填43–128 个 base64url 字符
code_challenge_method随 challenge只支持 S256
policy是ask 或 trust
selections是1–32 项 { "vault_id": "…", "mode": 1 };mode: 2 返回 400 mode_retired

响应:{ "code": "…", "state": "…" }。授权码 10 分钟有效、只能使用一次。

令牌

POST /api/oauth/token

不需要会话。public client 不得携带 client_secret(携带时返回 400 invalid_client)。

授权码换令牌:

字段必填说明
grant_type否authorization_code(缺省)
code是回调中的授权码
client_id是
redirect_uri是与授权时一致
code_verifierpublic 必填43–128 个字符,[A-Za-z0-9-._~]
client_secretconfidential 必填
POST /api/oauth/token HTTP/1.1
Content-Type: application/json

{
  "grant_type": "authorization_code",
  "code": "q8V0…",
  "client_id": "pmc_Xk3vQ9pL2mN8rT4w",
  "redirect_uri": "com.example.ai://mimikey/callback",
  "code_verifier": "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
}
{
  "access_token": "3q2-7wEXAMPLEACCESSTOKEN",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "Zp9-EXAMPLEREFRESHTOKEN",
  "refresh_expires_in": 2592000,
  "grant_id": "6f1d2c3b-4a59-4e68-8b7c-9d0e1f2a3b4c",
  "account_id": "0b7e…",
  "vaults": [{ "vault_id": "8d4c…", "mode": 1 }],
  "client_type": "public",
  "account_identity_public_key": "9Xwg4YSHxLN9dr2JSRYL_wfRh_0iafGiNzQtT5blNys"
}

注意 授权码在第一次兑换尝试时即被消耗。PKCE 校验失败(invalid_grant)、回调地址不符等错误之后,同一授权码不能再用,需要重新授权。

续期:

字段必填说明
grant_type是refresh_token
refresh_token是
client_id是
client_secretconfidential 必填

续期响应与上例相同,但不含 vaults(授权范围不会因续期改变)。

  • 轮换: 每次续期都会作废所用的 refresh token 并签发新的;新 refresh token 自本次续期起 30 天有效。
  • 复用检测: 已作废的 refresh token 再次出现时返回 401 refresh_token_reused,并立即吊销该授权的全部令牌。收到后必须停止自动重试,引导用户重新授权。
  • 续期不需要签名;但续期得到的 access token 仍只能配合审批钥签名使用。
响应字段说明
access_token访问令牌,3600 秒有效
refresh_token续期令牌,30 天滑动有效
grant_id授权 ID,参与结果与补丁的 AAD
account_id授权用户的账号 ID
vaults授权的库(仅授权码兑换时返回)
client_typepublic 或 confidential
account_identity_public_key账号身份公钥(X25519),用于密封修改补丁;账号尚未生成身份密钥时为 null

撤销

POST /api/oauth/revoke

撤销某个令牌所属的授权。幂等:未知令牌也返回 { "ok": true }。

client 类型请求体认证
public{ "token": "<access_token>", "client_id": "…" }用该授权的审批私钥签名(请求头同签名)
confidential{ "token": "…", "client_id": "…", "client_secret": "…" }client_secret

撤销后,该授权的 access token 与 refresh token 立即失效,未完成的请求全部置为 expired。

账号

GET /api/oauth/account

签名调用。返回授权用户的账号身份公钥与本授权的库。

{
  "account_id": "0b7e…",
  "identity_public_key": "9Xwg4YSHxLN9dr2JSRYL_wfRh_0iafGiNzQtT5blNys",
  "vaults": [
    { "vault_id": "8d4c…", "name": null, "kind": "personal", "mode": 1 }
  ]
}

identity_public_key 在账号尚未生成身份密钥时可能为 null,此时无法发起 update_item。

请求

请求对象

POST /api/oauth/requests、PUT …/patch 与 GET /api/oauth/requests/{id} 返回同一种对象(创建时只含部分字段):

字段说明
request_id请求 ID
statedraft、pending、approved、completed、denied、expired、cancelled
op操作类型(仅 GET)
vault_id目标库;pick_item 跨库时为 null(仅 GET)
item_id目标条目(仅 GET)
created_at、expires_at、state_changed_at时间
waiting_on、waiting_reason当前轮到谁,见下表
existingcreate_item 去重命中已有请求时为 true(仅创建)
deny_reasondenied 时设备给出的原因,例如 conflict(可能缺省)
result_enccompleted 时的密封结果(pick_item、get_item、list_items、create_item)
picked_itempick_item 完成时:{ id, title, url, kind }
created_itemcreate_item 完成时:{ id, title, url, kind }
saved_out_of_scopecreate_item 存到授权范围外时为 true,此时没有 result_enc 和 created_item
updated_itemupdate_item 完成时:{ id, revision },没有 result_enc

状态与等待

状态waiting_onwaiting_reason含义
draftclientpatchupdate_item 等你上传补丁
pending(get_item / list_items,ask)userapproval等用户批准
pending(pick_item)userpick等用户选条目
pending(create_item)userinput等用户在安全卡上填写
pending(update_item)userreview等用户确认修改
approveddeviceno_unlocked_device已批准,等任一已解锁的设备执行
completed / denied / expired / cancellednullnull已结束

选条目、新建与修改请求没有 approved 中间状态:卡上的操作本身就是批准。服务器在读取时惰性判定过期:超过 expires_at 的未结束请求会显示为 expired。

POST /api/oauth/requests

签名调用。创建一个请求。

通用字段说明
oppick_item、get_item、create_item、update_item、list_items
vault_id目标库,必须在本授权内且为模式 1;pick_item 可为 null
reason可选,显示在卡上,最多 500 个字符(超出部分截断),不得包含凭据

每个授权最多同时有 100 个未结束的请求(含草稿),超过时返回 429 too_many_requests。

pick_item

字段说明
vault_idnull 或省略:在本授权的全部库中挑选
hint可选,{ "url", "title", "kind" },只影响候选排序
{ "op": "pick_item", "vault_id": null, "hint": { "url": "https://bank.example.com", "kind": "login" }, "reason": "登录示例银行完成对账" }
{ "request_id": "a1b2c3d4-…", "state": "pending", "waiting_on": "user", "waiting_reason": "pick", "expires_at": "2026-10-23T08:40:00Z", "state_changed_at": "2026-10-23T08:30:00Z" }

有效期 10 分钟。在 trust 策略下也需要用户选择。

get_item

字段说明
vault_id必填
item_id必填

有效期 10 分钟。ask 策略下初始为 pending,trust 策略下初始为 approved。

create_item

字段说明
vault_id必填,默认保存位置
kindlogin(缺省)、credit_card、bank_card、api_key、server、secure_note
prefill可选,{ "title", "url", "username_hint", "extra_hint": { … } };也接受 v2 的字段名 site
{ "op": "create_item", "vault_id": "8d4c…", "kind": "login", "prefill": { "title": "示例银行", "url": "https://bank.example.com/login", "username_hint": "me@example.com" }, "reason": "登录示例银行完成对账" }

有效期 30 分钟,初始恒为 pending。同一授权、同一个库中已有网址主机名相同的待处理新建请求时,返回该请求并带 "existing": true。

update_item

字段说明
vault_id必填
item_id必填
fields必填,要修改的字段名(1–32 个,每个匹配 ^[a-z][a-z0-9_]*$,最长 64),例如 ["password"];服务器可见,用于卡上预告

创建时不得携带 patch_enc。请求以 draft 状态创建(有效期 60 秒,不出现在用户设备上),随后用 PUT …/patch 上传补丁。

{ "op": "update_item", "vault_id": "8d4c…", "item_id": "3f6b8f7e-…", "fields": ["password"], "reason": "已在网站上更换密码,同步到 MimiKey" }
{ "request_id": "a1b2c3d4-…", "state": "draft", "waiting_on": "client", "waiting_reason": "patch", "expires_at": "2026-10-23T08:31:00Z", "state_changed_at": "2026-10-23T08:30:00Z" }

list_items

字段说明
vault_id必填

结果只含元数据(不含密码与备注)。Swift SDK 不提供此操作。

上传修改补丁

PUT /api/oauth/requests/{id}/patch

签名调用。为 draft 状态的 update_item 请求上传密封补丁,请求随即变为 pending,有效期重置为 30 分钟。

字段说明
patch_encsealed box JSON 字符串,不超过 64 KiB

补丁的构造:

  • 收件人:account_identity_public_key(或 GET /api/oauth/account 的 identity_public_key)
  • AAD:password-manager/oauth-patch/v1/{grant_id}/{request_id}
  • 明文:{"patch":{"password":"新值"}},只能包含 fields 中声明的字段
{ "patch_enc": "{\"format\":1,\"epk\":\"PHld72bS…\",\"envelope\":{\"format\":2,\"nonce\":\"8Ovm4dzX…\",\"ciphertext\":\"9eSfVW9L…\"}}" }

响应为请求对象,state 为 pending、waiting_reason 为 review。错误:invalid_patch(格式不对或过大)、request_not_found、409 request_not_draft(不是草稿、已过期或不是 update_item)。

GET /api/oauth/requests/{id}

签名调用。查询请求;完成后携带结果。建议每 2 秒轮询一次,并在 App 回到前台时立即查询。

{
  "request_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
  "state": "completed",
  "op": "pick_item",
  "vault_id": null,
  "item_id": null,
  "created_at": "2026-10-23T08:30:00Z",
  "expires_at": "2026-10-23T08:40:00Z",
  "state_changed_at": "2026-10-23T08:31:12Z",
  "waiting_on": null,
  "waiting_reason": null,
  "result_enc": "{\"format\":1,\"epk\":\"FiF4WQXs…\",\"envelope\":{…}}",
  "picked_item": { "id": "3f6b8f7e-…", "title": "示例银行", "url": "https://bank.example.com/login", "kind": "login" }
}

用 seal 私钥、AAD password-manager/oauth-result/v1/{grant_id}/{request_id} 打开 result_enc:

{
  "item": {
    "id": "3f6b8f7e-9c1d-4e2a-8b3c-4d5e6f7a8b9c",
    "revision": 4,
    "record": {
      "title": "示例银行",
      "username": "me@example.com",
      "password": "correct horse battery staple",
      "url": "https://bank.example.com/login",
      "notes": "",
      "kind": "login",
      "extra": {},
      "updated_at": "2026-10-23T08:00:00.000Z"
    }
  }
}

list_items 的结果为 { "v": 2, "items": [{ "id", "meta": { "title", "username", "url", "kind", "updated_at" } }] }。

POST /api/oauth/requests/{id}/cancel

签名调用,无请求体。取消一个未结束的请求(draft、pending 或 approved)。响应:{ "request_id": "…", "state": "cancelled" }。已结束的请求返回 409 request_not_pending。

旧模式 2 端点

以下端点仅为既有的模式 2 授权保留只读兼容,新授权无法使用:

端点说明
GET /api/oauth/vaults签名调用;列出本授权的库与模式
GET /api/oauth/vaults/{vault_id}/items签名调用;返回库钥包裹与该库全部条目密文。模式 1 的库返回 403 mode_not_granted

设备端端点

以下端点由 MimiKey App 用用户的账号会话调用,接入方不会用到:

端点说明
GET /api/oauth/device/requests待本账号处理的请求(不含草稿)
GET /api/oauth/device/requests/{id}单个请求(深链出卡用)
POST /api/oauth/device/requests/{id}/decision{ "decision": "approve" 或 "deny", "reason": "conflict" };卡类请求(选条目、新建、修改)只能拒绝
POST /api/oauth/device/requests/{id}/result上传 result_enc 及回执 picked_item、created_item、updated_item 或 saved_out_of_scope

用户授权管理

端点说明
GET /api/oauth/grants当前用户的授权列表
GET /api/oauth/grants/{id}/history授权的使用历史与近 30 天统计
POST /api/oauth/grants/{id}/policy{ "policy": "ask" 或 "trust" },只影响之后的新请求
DELETE /api/oauth/grants/{id}撤销授权,立即生效

运营端:停用接入方

仅平台运营方可用,认证为独立的管理员会话(与用户会话隔离)。

端点说明
GET /api/admin/oauth/clients?query=&page=&page_size=分页列出全部 client(按 ID、名称、所有者邮箱搜索),含授权数、近 30 天请求数
GET /api/admin/oauth/clients/{id}明细、近 30 天按操作与状态的计数、最近 50 个请求(不含条目内容与结果)
POST /api/admin/oauth/clients/{id}/disable{ "reason": "…" }(必填,1–500 字符)
POST /api/admin/oauth/clients/{id}/enable{ "reason": "…" }

停用的效果:

  • 该 client 的全部授权撤销,全部令牌失效,未完成的请求全部置为 expired;
  • 之后的签名调用返回 401 client_disabled;
  • authorize/info 与 authorize 返回 403 client_disabled,MimiKey App 拒绝该 client 的深链;
  • 操作写入管理审计日志。

enable 只解除停用标记,不会恢复已撤销的授权;用户需要重新授权。

最后更新: 2026-10-23