跳到主要内容

端点参考

基础 URL:https://api.nanako.org。同样的路径在 https://www.nanako.org 上也可用。


GET /oauth/authorize​

流程的起点。这是一次浏览器跳转,不是你服务端发出的请求。

认证方式: 无。

参数位置必填默认值说明
client_idquery是—
redirect_uriquery是—与某个已注册地址逐字节一致
response_typequery否code也只接受 code
scopequery否openid profile空格分隔;请显式传
statequery否空原样拼回回调地址,不转义 —— 请保持 URL 安全
code_challengequery否空PKCE
code_challenge_methodquery否plain只有完全一致的 S256 才启用 SHA-256
promptquery否空none 表示请求一次无界面尝试

成功: 302 跳转到 Nanako 的登录与授权页。此时还没有授权码 —— 授权码是在 用户同意之后才到达你的回调地址的。

错误: 在这个端点以 JSON 返回,不会重定向到你的回调地址。见 错误。

prompt=none​

请求 Nanako 在不展示任何界面的情况下完成流程。Nanako 会重定向到你的回调地址并带上 错误,而不是渲染页面:

情况回调收到
未登录?error=login_required
已登录,但没有匹配的授权记录?error=consent_required
授权检查本身失败?error=interaction_required
已登录且有匹配的授权记录?code=… —— 不展示授权页

以上每种情况都会带回 state。收到这三种错误中的任意一种时,去掉 prompt=none 重试一次,回落到有界面的流程。

这是一次顶层跳转,不能放在 iframe 里:Nanako 在每个响应上都发送 X-Frame-Options: DENY 和禁止内嵌的 CSP,所以某些提供方常见的隐藏 iframe 方案在 这里行不通。


POST /oauth/token​

用授权码换取令牌,或刷新已有的一对令牌。

认证方式: client_secret_post;授权码 grant 也可用 PKCE。不支持 HTTP Basic。

Content-Type: application/x-www-form-urlencoded。JSON 请求体不会被读取。

grant_type=authorization_code​

参数必填说明
grant_type是authorization_code
code是来自回调,10 分钟内,未使用过
client_id是
redirect_uri是与 /oauth/authorize 那次逐字节一致
client_secret条件除非授权码是带 code_challenge 签发的,否则必填
code_verifier条件授权码是带 code_challenge 签发时必填

需要哪种凭证,由授权码当初是怎么签发的决定,而不是由你发什么决定。没走 PKCE 签发的码要 client secret;走了 PKCE 签发的码要 verifier。

grant_type=refresh_token​

参数必填说明
grant_type是refresh_token
refresh_token是最近一次签发的那个
client_id是
client_secret是始终必填 —— 这个 grant 没有 PKCE 分支

响应​

两种 grant 都返回 200:

{
"access_token": "aB3dE5fG7hJ9kL1mN3pQ5rS7tU9vW1xY3zA5bC7dE9=",
"token_type": "Bearer",
"expires_in": 7200,
"refresh_token": "zY9xW7vU5tS3rQ1pN9mL7kJ5hG3fE1dC9bA7zY5xW3=",
"scope": "profile email"
}
字段类型说明
access_tokenstring不透明字符串,44 个字符。有效期 2 小时。
token_typestring固定为 Bearer
expires_innumber固定为 7200
refresh_tokenstring每次调用都是新的,上一个此刻已作废。
scopestring实际授予的 scope

没有 id_token。见不支持的能力。


GET /oauth/userinfo​

返回已登录用户的资料。

认证方式: Authorization: Bearer <access_token>。前缀区分大小写。

响应: 200。sub 必然存在;其余每个 claim 取决于令牌的 scope,以及用户是否 填过对应的值。

{
"sub": "0198c4a1-7f3e-7b21-9d04-1a2b3c4d5e6f",
"name": "hana",
"picture": "https://cdn.nanako.org/avatars/…",
"email": "hana@example.com",
"email_verified": true,
"phone_number": "+8613800138000",
"phone_number_verified": true
}
Claim所需 scope说明
sub无稳定且永久的用户标识。用它作为你记录的主键。
nameprofile昵称;用户可以改
pictureprofile头像 URL;未设置时为空字符串
emailemail账号没有邮箱时整个字段不出现
email_verifiedemail
phone_numberphone账号没有手机号时整个字段不出现
phone_number_verifiedphone
sub 有两种格式

较新创建的账号,sub 是 UUID(0198c4a1-7f3e-7b21-9d04-1a2b3c4d5e6f)。早于一次 schema 迁移的账号,保留的是数字形式(42)。两种对各自账号都是稳定且永久的。

请把 sub 当作不透明字符串:不要解析它,也不要假定它是 UUID。


POST /oauth/revoke​

撤销一个令牌。

认证方式: client_secret_post。客户端凭证是必填的,因此公开(PKCE)客户端无法 调用这个端点。

Content-Type: application/x-www-form-urlencoded。

参数必填说明
client_id是
client_secret是
token是要撤销的令牌
token_type_hint条件撤销 refresh token 时必填:refresh_token

响应: 200 {}。

成功响应并不代表真的撤销了什么。令牌不存在、属于别的应用、或者没带对应的 token_type_hint,三种情况返回的都是同一个 200 {}。前两种是刻意为之 —— 避免泄露 哪些令牌存在 —— 但这也意味着 hint 非常关键。请分两次调用分别撤销 access token 和 refresh token,每次显式带上对应的 hint。


GET /.well-known/openid-configuration​

返回提供方元数据。

这份文档里的端点地址是错的

token_endpoint、userinfo_endpoint 和 revocation_endpoint 上带了一个并不存在的 /api/v1 前缀,三个地址都会返回 404。issuer 也与这份文档实际所在的主机不一致, 一些严格的 OIDC 库会因此直接拒绝整份文档。

不要把客户端库指向这个 URL,请照本页配置端点。

供参考:文档里 scopes_supported 为 openid、profile、email、phone; response_types_supported 为 code;grant_types_supported 为 authorization_code 与 refresh_token;token_endpoint_auth_methods_supported 为 client_secret_post;code_challenge_methods_supported 为 plain 与 S256。 这五项是准确的。

文档里还有 jwks_uri 和 id_token_signing_alg_values_supported: ["RS256"]。这两项 请忽略 —— 系统不签发 id_token,而那个 URL 上的密钥属于一个不相关的内部集成。