OAuth 接入指南
欢迎来到落雪咖啡屋 maimai DX 查分器的 OAuth 接入指南!
在这里,你将了解如何使用 OAuth 2.0 获取用户授权、调用查分器 API,以及通过 OpenID Connect(OIDC)完成用户登录和身份识别。
介绍
本查分器推出 OAuth 旨在替代个人 API 密钥,提供更安全、灵活的方式供开发者访问用户数据。
个人 API 密钥虽然可以访问用户数据,但是存在安全隐患:如果密钥泄露,其他人可以随意访问用户数据。而 OAuth 则提供了更细粒度的权限控制和更安全的授权流程。
开发者可以通过 OAuth 获取访问令牌(Access Token),并使用此令牌访问用户授权的数据。需要登录能力时,还可以请求 OIDC 权限并获取 ID Token,以安全地识别当前用户。用户可以随时撤销授权,确保数据安全。
接入步骤
接入前,请确保你已经申请并成为了开发者。申请时使用的开发者信息将会在 OAuth 授权页面中展示。
1. 创建应用
前往开发者面板创建一个新的 OAuth 应用。你需要提供以下信息:
- 应用名称:你的应用名称,将在授权页面中显示。
- 应用描述(可选):简要描述你的应用功能。
- 应用图标(可选):上传一个应用图标,将在授权页面中显示。
- 回调地址:授权完成后用户将被重定向到请求指定的地址。每个应用最多可以登记 10 个回调地址。
- 应用权限:根据用途选择 API 授权或 OpenID Connect 身份认证所需的权限。
授权请求中的 redirect_uri 必须与已登记的某个回调地址完全一致。Web 应用应使用 HTTPS;本地开发可以使用 localhost、127.0.0.1 或 [::1] 的 HTTP 地址。
权限范围
应用权限分为两组:
| 分组 | 权限 | 用途 |
|---|---|---|
| API 授权 | read_user_profile | 通过 API 读取用户信息 |
| API 授权 | read_player | 读取玩家信息、谱面成绩和历史成绩 |
| API 授权 | write_player | 更新玩家信息、上传或删除成绩 |
| API 授权 | read_user_token | 读取个人 API 密钥,通常不建议申请 |
| 登录与身份 | openid | 验证用户身份并获取稳定的用户标识 sub |
| 登录与身份 | profile | 获取用户名等基本资料,必须同时申请 openid |
| 登录与身份 | 获取用户绑定的邮箱地址,必须同时申请 openid |
2. 获取 OAuth 授权链接
创建应用后,你将获得一个 OAuth 授权链接。用户可以通过此链接授权你的应用访问其游戏数据。
链接格式如下:
常用参数如下:
| 参数名 | 必填 | 说明 |
|---|---|---|
| response_type | 是 | 固定为 code |
| client_id | 是 | 创建应用后获得的应用 ID |
| redirect_uri | 是 | 本次授权使用的回调地址,必须与已登记地址完全一致 |
| scope | 是 | 以空格分隔的权限列表 |
| state | 推荐 | 用于关联请求和回调,并防止跨站请求伪造 |
| nonce | OIDC 推荐 | 绑定授权请求和 ID Token,防止重放攻击;最长 255 个字符 |
| code_challenge | 使用 PKCE 时必填 | 由 code_verifier 计算得到的挑战值 |
| code_challenge_method | 使用 PKCE 时必填 | 固定为 S256 |
仅调用查分器 API 的示例:
使用查分器账号登录的示例:
3. 用户授权
用户点击授权链接后,将被重定向到授权页面。在此页面,用户登录查分器账号后可以查看你的应用信息,并选择是否授权。
如果用户同意授权,将会被重定向到本次请求的回调地址,并附带一个授权码。如果应用配置为无回调地址,则会直接显示授权码(形如 JVJ6-VPTM-MGHZ)。回调中的 state 应与授权请求中发送的值完全一致。
4. 使用授权码获取访问令牌
在你的回调地址处理授权码后,你需要使用此授权码向 OAuth 服务器请求访问令牌。你可以使用以下 API 端点:
请求参数
参见访问令牌请求方式。
响应体
| 字段名 | 类型 | 说明 |
|---|---|---|
| access_token | string | 访问令牌,用于访问用户数据 |
| token_type | string | 令牌类型,通常为 Bearer |
| expires_in | integer | 访问令牌的有效期,单位为秒 |
| refresh_token | string | 刷新令牌,用于获取新的访问令牌 |
| scope | string | 授权范围,表示应用可以访问的权限 |
| id_token | string | OIDC ID Token,仅在授权范围包含 openid 时返回 |
响应示例
错误响应
如果请求参数无效、授权码过期或其他错误,服务器将返回一个错误响应,包含 error 和 error_description 字段。例如:
error 取值如下:
| error | 说明 |
|---|---|
| invalid_request | 缺少必需参数、参数无效或请求格式错误 |
| invalid_client | 客户端不存在,或客户端认证(client_secret)失败 |
| invalid_grant | 授权码或刷新令牌无效、已过期,或与客户端、回调地址不匹配 |
| unsupported_grant_type | 不支持的 grant_type |
| server_error | 服务器内部错误 |
5. 使用访问令牌访问 API
使用获取到的访问令牌,你可以访问用户的游戏数据。你需要在请求头中添加 Authorization 字段,格式为 Bearer [access_token]。
例如,获取用户的舞萌 DX 游戏数据:
其他接口可以参考 OAuth API 文档。
6. 刷新访问令牌
如果访问令牌过期,你可以使用刷新令牌获取新的访问令牌。你需要向以下端点发送请求:
请求参数
| 参数名 | 类型 | 说明 |
|---|---|---|
| client_id | string | 应用 ID |
| client_secret | string | 机密客户端的应用密钥;使用 PKCE 时可省略 |
| grant_type | string | 授权类型,固定为 refresh_token |
| refresh_token | string | 从上一步获取的刷新令牌 |
请求示例
响应体
响应体与获取访问令牌时相同,将包含新的访问令牌和刷新令牌。
授权范围包含 openid 时,刷新令牌响应也会包含新的 id_token。刷新流程生成的 ID Token 不包含初次授权请求中的 nonce。
使用 OpenID Connect
OIDC 使用与 OAuth 相同的授权码流程。客户端无需硬编码各个端点,可以从发现文档读取服务端元数据:
发现文档包含 issuer、authorization_endpoint、token_endpoint、userinfo_endpoint 和 jwks_uri 等字段。OAuth 授权服务器元数据也可以从以下地址获取:
验证 ID Token
ID Token 使用 RS256 签名。客户端应根据发现文档中的 jwks_uri 获取公钥,并至少完成以下校验:
- 使用 JWKS 中与 Token kid 对应的公钥验证签名和 RS256 算法。
- 确认 iss 与发现文档中的 issuer 完全一致。
- 确认 aud 包含当前应用的 client_id。
- 确认 exp 尚未过期。
- 如果授权请求发送了 nonce,确认 Token 中的 nonce 与请求值一致。
sub 是当前用户在查分器中的稳定标识,客户端应使用 iss 与 sub 的组合作为外部账号标识,不要使用用户名或邮箱作为唯一标识。
获取用户信息
授权范围包含 openid 时,可以使用访问令牌调用 UserInfo 端点:
响应中的声明由已授权权限决定:
| 权限 | 返回的声明 |
|---|---|
| openid | sub |
| profile | name、preferred_username |
| email、email_verified |
响应示例:
email_verified 会反映用户当前绑定邮箱的实际验证状态。用户尚未验证邮箱时,该字段为 false。
访问令牌请求方式
授权码兑换支持应用密钥和 PKCE(Proof Key for Code Exchange)。公共客户端使用 PKCE,机密客户端使用应用密钥。授权请求带有 code_challenge 时,兑换授权码必须提交对应的 code_verifier;请求同时带有 client_secret 时,服务器会校验两项。未使用 PKCE 时,兑换授权码必须提交有效的 client_secret。
应用密钥
机密客户端(如服务器端应用)可以使用应用密钥认证来获取访问令牌。此方式需要在请求中提供有效的 client_secret。
请求参数
| 参数名 | 类型 | 说明 |
|---|---|---|
| client_id | string | 应用 ID |
| client_secret | string | 应用密钥 |
| grant_type | string | 授权类型,固定为 authorization_code |
| code | string | 从回调地址获取的授权码 |
| redirect_uri | string | 必须与授权请求及某个已登记的回调地址完全一致 |
请求示例
示例代码(Python)
以下是一个使用应用密钥获取访问令牌的示例代码,演示如何处理 OAuth 授权流程:
PKCE
PKCE(Proof Key for Code Exchange)是一种增强 OAuth 2.0 安全性的机制,特别适用于公共客户端(如移动应用、单页应用等),可以防止授权码被截获和重放攻击。
PKCE 通过在授权请求中添加一个随机生成的 code_verifier 和 code_challenge 来实现。以下是 PKCE 的基本流程:
- 生成 Code Verifier:客户端生成一个随机字符串,称为 code_verifier。
- 生成 Code Challenge:客户端使用 code_verifier 生成一个 code_challenge,通常是通过 SHA-256 哈希算法。
- 发送授权请求:在授权请求中,客户端将 code_challenge 和 code_challenge_method(通常为 S256)作为参数发送。
- 获取授权码:用户授权后,服务器将 code 返回给客户端。
- 交换访问令牌:客户端使用 code_verifier 和 code 向服务器请求访问令牌。
请求参数
| 参数名 | 类型 | 说明 |
|---|---|---|
| client_id | string | 应用 ID |
| grant_type | string | 授权类型,固定为 authorization_code |
| code | string | 从回调地址获取的授权码 |
| redirect_uri | string | 必须与授权请求及某个已登记的回调地址完全一致 |
| code_verifier | string | 生成授权请求时保存的原始验证字符串 |
请求示例
示例代码(Python)
以下是一个使用 PKCE 的示例代码,演示如何生成 code_verifier 和 code_challenge,并在授权请求中使用它们: