跳到主要内容

身份验证

身份验证

Authorization 请求头中的密钥,是您访问自己数据的方式。除此之外没有别的入口,任何请求都不会通过会话 Cookie 完成身份验证。类别端点是例外:它们描述的是我们索取的字段,而不是任何人的数据,因此不带密钥也能应答。

密钥

密钥只在创建的那一刻显示一次。请把它放在服务器读得到、浏览器读不到的地方。

  • pc_sk_live_…
  • pc_sk_test_…

测试与正式环境彼此隔离

模式写在凭据本身,而不是作为参数传入,因此测试密钥既无法读取也无法修改您真实的产品护照。走错方向的请求会直接失败,而不会落到另一份目录上。测试护照会在护照本身最后一次修改 90 天后删除。

发送密钥

通过 HTTPS 使用 Bearer 身份验证。其他方式一律不接受;缺少该请求头的请求,在触及您的任何记录之前就会被拒绝。

请求
curl "https://passportcraft.com/api/v1/whoami" \
  -H "Authorization: Bearer pc_sk_live_…"

浏览器端凭据是刻意不予支持的。密钥属于服务器;出现在前端代码里的密钥,等同于已经公开的密钥。

作用域

密钥携带一组固定的作用域,在创建时选定。请求若缺少所调用端点要求的作用域即被拒绝,拒绝信息会指明缺少哪一项权限。

无需作用域

有效凭据即可。这些端点不会返回任何属于某一个组织的数据。

无需凭据

无需密钥即可读取。这些端点描述的是产品本身——有哪些类别、每个类别索取哪些字段——因此对每一位读者的答复都相同。仍然接受携带密钥的请求;已吊销或已过期的密钥,仍然会被拒绝。不带密钥时,按每个客户端地址每分钟 60 次请求的匿名计数器计量。

一个密钥,多个组织

一个密钥属于一个组织,只有在持有某项授权时,才能对另一个组织执行操作。管理客户的组织可以为自己的密钥授予访问各个客户的权限,且权限按客户分别设置。每个请求都在其路径中指定一个组织,且只对该组织生效。

授予密钥对某个客户的访问权限

调用 POST /organizations/{organization}/grants,在路径中提供客户的标识符,并在请求体中提供密钥的 key_id 以及要授予的作用域。发起调用的密钥须在其所属组织中持有 keys:admin。只能为客户授予该密钥在其所属组织中已经持有的作用域;filings:write 还要求先确认已获得该客户的书面授权。

请求
curl "https://passportcraft.com/api/v1/organizations/{client}/grants" \
  -X POST \
  -H "Authorization: Bearer $PASSPORTCRAFT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"key_id": "K3n9Qw2P", "scopes": ["passports:read", "passports:write"]}'

访问权限终止时

DELETE /organizations/{organization}/grants/{keyId} 会终止一项授权,该密钥在持有授权的其他每个组织中仍可正常使用。客户也可以在自己的设置中移除某个密钥的访问权限。客户与您的组织之间的合作关系一旦终止,该客户名下的所有授权会同时全部终止。此后,针对该客户的每个请求都会收到 404 organization_not_found_or_not_granted,这与面向不存在的组织返回的答案相同。若要区分这两种情况,请调用 GET /organizations:已失去访问权限的组织不会再出现在列表中。

GET /organizations 中的每一条记录都包含 display_name(组织名称)和 link_status:对您组织管理的客户,link_status 为 accepted;对您自己的组织,则为 null。

管理客户的组织的正式密钥

当管理客户的组织自身的套餐,或其至少一个客户的套餐,是付费的 Pro 或 Scale 套餐时,该组织就可以创建正式密钥。处于试用期或存在逾期款项的套餐不计入其中。每个请求仍会依据路径中指定组织的套餐进行核验。

示例:一个调度任务,十个客户

一个夜间任务调用 GET /organizations,列出其密钥可以操作的组织,然后分别同步每个客户。一旦某个客户离开,就会从列表中消失,该任务也就不再调用它,而不会每晚都记录一次 404。

请求
const base = 'https://passportcraft.com/api/v1'
const headers = { 'Authorization': 'Bearer ' + process.env.PASSPORTCRAFT_API_KEY }

const { data } = await (await fetch(base + '/organizations', { headers })).json()
const clients = data.filter((connection) => connection.link_status === 'accepted')

for (const client of clients) {
  // One organization per request, always named in the path.
  await syncCatalogue(client.organization_id, client.display_name)
}