一套账号,登录所有 Current 系产品。第三方应用全程拿不到用户密码:用户在我们的页面上授权,
应用只拿到一个只能读取被授权信息的令牌。
Issuer https://music.20110208.xyz/cm
账户中心与开发者入口:打开控制台。控制台可查阅 CurrentMusic 自有后端与网易云侧完整接口目录,并管理应用登记。
用户 应用(你的产品) CurrentStation 通行证
│ │ │
│ 点击「用通行证登录」 │
│─────────────────▶│ 302 /passport/authorize │
│ │ ?client_id&redirect_uri │
│ │ &scope&state&code_challenge │
│ │─────────────────────────────▶│ 未登录?先在本站登录
│◀─────────────────────────────────────────────── │ 展示同意页(应用名 + 申请的权限)
│ 点「同意」 │ │
│───────────────────────────────────────────────▶ │ 生成一次性 code(60 秒)
│ │◀── 302 redirect_uri?code=…&state=… ─┘
│ │ POST /passport/token │
│ │ code + code_verifier │
│ │─────────────────────────────▶│ 校验 PKCE → 发 access_token
│ │◀── {access_token, refresh_token?, sub} ─┘
│ │ GET /passport/userinfo │
│ │ Authorization: Bearer … │
│ │─────────────────────────────▶│ 按 scope 返回昵称/头像/脱敏邮箱…
用管理员登录后的会话令牌调用;返回的 clientSecret 只显示这一次,请立刻存到服务端环境变量里。原生 App / 纯前端 SPA 请用 "public": true(不发密钥,强制 PKCE)。
curl -X POST https://music.20110208.xyz/cm/passport/apps \
-H "Authorization: Bearer <管理员会话令牌>" -H "Content-Type: application/json" \
-d '{"name":"CurrentSpace","redirects":["https://app.example.com/auth/callback"],
"scopes":"openid profile email offline_access","homepage":"https://app.example.com"}'
# 201
# { "clientId": "cs_app_1a2b3c4d5e6f", "clientSecret": "cs_sk_…", … }
生成 PKCE:verifier 为 43~128 位随机串,challenge = BASE64URL(SHA256(verifier))(去掉 =)。state 用于防 CSRF,回调时要原样校验。
# verifier / challenge 生成(浏览器 WebCrypto,Node 18+ 亦可)
const verifier = base64url(crypto.getRandomValues(new Uint8Array(32)));
const challenge = base64url(new Uint8Array(
await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier))));
location.href = 'https://music.20110208.xyz/cm/passport/authorize'
+ '?response_type=code'
+ '&client_id=cs_app_1a2b3c4d5e6f'
+ '&redirect_uri=' + encodeURIComponent('https://app.example.com/auth/callback')
+ '&scope=' + encodeURIComponent('openid profile email offline_access')
+ '&state=' + state
+ '&code_challenge=' + challenge + '&code_challenge_method=S256';
code 只能用一次、60 秒过期;换令牌时 redirect_uri 必须与授权时完全一致。有密钥的客户端用 Basic 认证,公开客户端只传 code_verifier。
curl -X POST https://music.20110208.xyz/cm/passport/token \
-H "Content-Type: application/json" \
-d '{"grant_type":"authorization_code","client_id":"cs_app_1a2b3c4d5e6f",
"client_secret":"cs_sk_…","code":"cs_code_…","redirect_uri":"https://app.example.com/auth/callback",
"code_verifier":"<PKCE verifier>"}'
# 200
# { "access_token":"cs_at_…", "token_type":"Bearer", "expires_in":7200,
# "scope":"openid profile email offline_access", "refresh_token":"cs_rt_…",
# "sub":"cs_1a2b3c4d5e6f" }
curl https://music.20110208.xyz/cm/passport/userinfo -H "Authorization: Bearer cs_at_…"
# 200
# { "sub":"cs_1a2b3c4d5e6f", "nickname":"小明", "avatar":"https://…",
# "email":"m***@qq.com", "scope":"openid profile email offline_access",
# "client_id":"cs_app_1a2b3c4d5e6f" }
开发者平台页面:GET /developer(也支持 /cm/developer,独立于音乐客户端)。
| 方法 | 路径 | 鉴权 | 说明 |
|---|---|---|---|
| GET | /cm/passport/meta | 公开 | 平台元信息:端点、scope 目录、令牌有效期、已接入产品 |
| GET | /cm/passport/docs | 公开 | 本页 |
| POST | /cm/passport/apps | 登录用户 | 登记应用 → clientId / clientSecret(仅此一次);每账号上限 5 个 |
| GET | /cm/passport/apps | 登录用户 | 我登记的应用(含在用授权数、配额);管理员加 ?all=1 看全部 |
| PUT | /cm/passport/apps/<client_id> | 所有者 | 改名称/主页/回调/scope/公开↔机密/停用(切机密时返回新密钥一次) |
| POST | /cm/passport/apps/<client_id>/secret | 所有者 | 轮换 client_secret(只返回一次);可选 revokeGrants 同时撤销已发出的授权 |
| DELETE | /cm/passport/apps/<client_id> | 所有者 | 停用应用并撤销其全部授权 |
| GET | /cm/passport/authorize | 用户会话 | 同意页数据(应用名、申请的权限、当前用户;未登录返回 401) |
| POST | /cm/passport/authorize | 用户会话 | 批准/拒绝 → 返回带 code 或 error 的回调地址 |
| POST | /cm/passport/token | 客户端 | authorization_code / refresh_token / client_credentials |
| GET | /cm/passport/userinfo | Bearer | 按 scope 返回用户信息 |
| POST | /cm/passport/introspect | 客户端 | 校验令牌是否有效(RFC 7662) |
| POST | /cm/passport/revoke | 客户端/用户 | 撤销令牌(RFC 7009,幂等) |
| GET | /cm/passport/grants | 用户会话 | 我授权过的应用 |
| DELETE | /cm/passport/grants/<授权id> | 用户会话 | 解除某个应用的授权(其令牌立即失效) |
| GET | /cm/passport/events | 管理员 | 审计流水(授权/换令牌/撤销/异常) |
账号本体(注册、登录、退出、在线设备)仍在 /cm/auth/*,与通行证共用同一批账号;现有 App 与网页无需改动。
| scope | 含义 | 返回字段 |
|---|---|---|
openid | 登录身份(必需) | sub(cs_ 开头的稳定标识,不含邮箱/手机号) |
profile | 公开资料 | nickname avatar bio created_at |
email | 邮箱(脱敏) | email:a***@qq.com |
phone | 手机号(脱敏) | phone:138****8000 |
offline_access | 长期授权(30 天可刷新) | 额外返回 refresh_token |
申请未登记或超出应用上限的 scope 会得到 invalid_scope;用户看到的是每个 scope 的中文说明(同意页)。
cs_code_…cs_at_…Authorization: Bearer 头。只存哈希,撤销即时生效。cs_rt_…curl -X POST https://music.20110208.xyz/cm/passport/token -H "Content-Type: application/json" \
-d '{"grant_type":"refresh_token","client_id":"cs_app_…","client_secret":"cs_sk_…",
"refresh_token":"cs_rt_…"}'
只代表「应用自己」,没有用户信息;/userinfo 会对这类令牌返回 403。
curl -u "cs_app_…:cs_sk_…" -X POST https://music.20110208.xyz/cm/passport/token \
-H "Content-Type: application/json" -d '{"grant_type":"client_credentials","scope":"openid"}'
| error | HTTP | 含义 / 处理 |
|---|---|---|
invalid_client | 401 | client_id / client_secret 不对:检查是否把密钥写进了前端代码 |
invalid_request | 400 | 缺参数或格式不对(如公开客户端没带 PKCE) |
invalid_grant | 400 | code/refresh 无效、过期、已用过,或 PKCE/redirect_uri 不匹配 |
invalid_scope | 400 | 申请了未登记或超出应用上限的 scope |
unsupported_grant_type | 400 | grant_type 拼错 |
access_denied | 302 | 用户点了拒绝(回调地址上带 error=access_denied) |
invalid_token | 401 | /userinfo 令牌无效或过期(响应头带 WWW-Authenticate) |
| — | 429 | 令牌端点限流(同一客户端+IP 60 秒内 30 次) |
code_challenge;机密客户端的密钥只放服务端。不用找管理员:任何通行证账号都能自助把自己的应用接进来。
入口:https://music.20110208.xyz/developer —— 独立页面,不依赖 CurrentMusic 客户端,
用自己的通行证会话(浏览器 localStorage 里的 csdev.token)登录,不读写音乐客户端的登录态。
| 能力 | 说明 |
|---|---|
| 新建应用 | 填名称、应用主页、回调地址(每行一个,精确匹配)、需要的 scope,并选择「公开客户端(PKCE)」或「机密客户端(带密钥)」 |
| 获取凭据 | client_id 随时可见可复制;client_secret 只在创建或轮换时显示一次(服务端只存哈希,我们自己也拿不回来) |
| 获取令牌 | 平台内可一键用刚拿到的凭据试取应用令牌(client_credentials),并生成可直接粘贴的接入代码(授权码 + PKCE 三段) |
| 轮换密钥 | 密钥泄漏或遗失时一键换新(旧密钥立即失效);可勾选「同时撤销该应用已发出的授权」 |
| 配额与权限 | 每个账号最多 5 个应用;只能查看/修改/停用自己登记的应用(管理员可管理全部) |
第三方产品的用户数据只经 /passport/userinfo 按 scope 返回,应用侧拿不到密码,也不该把密钥写进前端代码。
| 版本 | 内容 |
|---|---|
| 1.2(v1.28.19) | 开发者平台从 CurrentMusic 客户端里独立出来:GET /developer 单文件页面(自带登录/注册、独立会话键 csdev.token),客户端不再内置该页,只保留一个外链入口 |
| 1.1(v1.28.18) | 新增 CurrentDeveloper 开发者平台:任何账号可自助登记应用(每账号 5 个)、填主页与回调、获取/轮换凭据、一键试取应用令牌、停用/启用;POST /passport/apps/{client_id}/secret |
| 1.0(v1.28.17) | 首个版本:授权码 + PKCE、刷新轮换、userinfo、introspect、revoke、应用登记、授权管理、审计、单点登出 |