身份验证

除自描述的根路径外,每个请求都需要带上一个请求头。

Authorization: Bearer <your api key>

令牌在您的个人中心生成。每个账户最多十个,每个都可以单独吊销,因此某个令牌泄露时,删除它不会影响其他令牌。使用 API 需要付费套餐:没有付费套餐时,除 / 和 /v1/account 外的所有接口都会返回 403 plan_required。

应用也可以通过 OAuth 2.1 为您获取令牌:您登录后查看它申请的权限,然后点击“允许”。这类令牌放在同一个请求头中,用法完全相同。

为什么不用 ?key=

放在查询字符串中的密钥,会出现在您意想不到的地方:Web 服务器访问日志、浏览器历史记录、代理日志,以及响应中链接到的任何资源的 Referer 请求头。因此 API 不接受这种方式,并会返回 401 missing_key 予以说明。

主站上的旧版 ?export= URL 仍然接受 ?key=,因为多年前编写的脚本依赖它,取消支持会导致这些脚本失效。这是唯一接受它的地方,详见旧版导出 URL。

检查令牌是否可用

/v1/account 是成本最低的调用:它不消耗配额,即使账户没有套餐也能使用,因此既能回答“这个令牌是否有效”,也能回答“我有哪些权限”。

curl -H "Authorization: Bearer $KEY" https://api.publicwww.com/v1/account
{
  "plan": "enterprise",
  "plan_until": 1819461840,
  "full_access": true,
  "quota": {
    "searches": { "limit": 300, "used": 12, "resets_at": 1787961600 },
    "snippets": { "limit": 100, "used": 3,  "resets_at": 1787961600 }
  },
  "limits": {
    "disclosed_positions": 4294967295,
    "max_per_page": 1000000,
    "max_per_page_snippets": 10000
  }
}

可能出现的错误

状态错误码含义
401missing_key缺少 Authorization: Bearer 请求头。查询字符串中的密钥不算数。
401invalid_key该令牌不对应任何账户。请检查是否混入了多余的换行符或引号。
403plan_required令牌本身没问题,但该账户没有付费套餐。

401 响应还会附带 WWW-Authenticate: Bearer 响应头,因此通用处理身份验证的 HTTP 客户端也能正确应对。

面向应用的 OAuth 2.1

代表他人工作的应用(助手、集成、托管服务)不应要求每位用户手动复制令牌,而应将用户引导至 PublicWWW:用户登录并批准该应用后,应用即可获得自己的令牌。该令牌与其他令牌一样以 Authorization: Bearer 发送,可访问整个 API 以及位于 https://api.publicwww.com/mcp 的 MCP 服务器,并受该账户的套餐、配额和速率限制约束。

项目地址
授权服务器元数据(RFC 8414)https://publicwww.com/.well-known/oauth-authorization-server
受保护资源元数据(RFC 9728)https://api.publicwww.com/.well-known/oauth-protected-resource
授权端点https://publicwww.com/oauth/authorize
令牌端点https://publicwww.com/oauth/token
吊销端点(RFC 7009)https://publicwww.com/oauth/revoke

识别应用

无需注册客户端。client_id 就是应用所发布的一个小型 JSON 文档的 https URL,即客户端元数据文档(client metadata document)。每次有人连接时,PublicWWW 都会读取该文档,因此应用名称和回调地址始终是最新的,批准授权的用户也能看到是哪个主机发布了它们。

{
  "client_id": "https://app.example.com/oauth/client.json",
  "client_name": "Example App",
  "redirect_uris": ["https://app.example.com/oauth/callback"],
  "grant_types": ["authorization_code"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "none"
}
  • 文档中的 client_id 必须与提供该文档的 URL 完全一致。文档通过 https 获取,URL 必须包含路径,且不跟随重定向;必须在 5 秒内响应,大小不超过 64 KB。
  • redirect_uris 必须是 https 地址;如果应用运行在用户自己的电脑上,也可以是 127.0.0.1、localhost 或 [::1] 上的 http 地址,此时任意端口均可匹配。不接受 myapp:// 之类的自定义协议。
  • 所有应用都是公共客户端:无论文档中声明的 token_endpoint_auth_method 是什么,令牌请求都不携带密钥。授权码改由 PKCE 保护。

授权流程

采用带 PKCE 的授权码模式,S256 是唯一支持的方法。将用户引导至授权端点:

https://publicwww.com/oauth/authorize
    ?response_type=code
    &client_id=https%3A%2F%2Fapp.example.com%2Foauth%2Fclient.json
    &redirect_uri=https%3A%2F%2Fapp.example.com%2Foauth%2Fcallback
    &code_challenge=<BASE64URL(SHA-256(code_verifier))>
    &code_challenge_method=S256
    &state=<random>

如果用户尚未登录,需先使用邮件发送的一次性验证码登录;随后会看到应用名称、其文档所在的主机以及将要返回的地址,并点击“允许”或“取消”。返回 redirect_uri 时会带上 code、您的 state 以及 iss=https://publicwww.com(RFC 9207)。授权码有效期为十分钟,且只能使用一次。用它换取令牌:

curl https://publicwww.com/oauth/token \
     -d grant_type=authorization_code \
     -d code="$CODE" \
     -d code_verifier="$VERIFIER" \
     -d client_id=https://app.example.com/oauth/client.json \
     -d redirect_uri=https://app.example.com/oauth/callback
{ "access_token": "<token>", "token_type": "Bearer", "scope": "mcp" }

scope 可以省略:只有一个作用域 mcp,它涵盖整个 API。resource(RFC 8707)同样可以省略;如果传入,其值应为 https://api.publicwww.com/mcp 或 https://api.publicwww.com。

令牌有效期

令牌在被吊销之前一直有效:没有过期时间,也没有刷新令牌。今天能用的集成,明天无需任何人干预也能继续使用。令牌只会在有意为之时被吊销:用户在个人中心断开该应用、应用自行吊销,或账户被删除。

curl https://publicwww.com/oauth/revoke \
     -d token="$TOKEN" \
     -d client_id=https://app.example.com/oauth/client.json

无论令牌是否存在,吊销端点始终返回 200。

OAuth 错误

环节错误码含义
授权错误页面无法读取 client_id 指向的文档,或文档中未列出 redirect_uri。此时不会将用户送回:未经验证的地址绝不跳转。
授权invalid_request缺少 code_challenge,或使用了 S256 以外的方法。
授权unsupported_response_type使用了 response_type=code 以外的值。
授权、令牌invalid_targetresource 不是本 API。
授权access_denied用户点击了“取消”。
令牌invalid_grant授权码未知、已使用、已过期或签发给了其他 client_id;或者 code_verifier 或 redirect_uri 不匹配。
令牌unsupported_grant_type使用了 authorization_code 以外的授权类型。

除错误页面外,授权错误会以 error、error_description、state 和 iss 参数返回到 redirect_uri;令牌错误则返回 400,并在 JSON 中包含同样的两个字段。

下一篇 发送请求