
最近在给项目接 Ace Data Cloud 的登录功能,官方文档写得很散,自己踩了几个坑才跑通。这篇把 OAuth 2.0 配合 PKCE 的完整流程梳理一遍,包括注册应用、生成授权链接、换取令牌、调 API,都覆盖到。
如果你需要代表用户访问 Ace Data Cloud 的资源,让用户手动复制 API Key 是个很糟糕的体验。更合理的做法是用 OAuth 2.0 授权码流程配合 PKCE:用户点登录、授权你请求的权限范围,你的应用拿到一个 access token,只能操作用户授权的那些资源。
这个集成方案适合第三方应用、Agent、MCP 客户端和自动化工作流。
认证基础 URL:
https://auth.acedata.cloud
核心端点列表:
| 用途 | 端点 |
|---|---|
| 发现文档 | GET /.well-known/oauth-authorization-server |
| 浏览器授权 | GET https://auth.acedata.cloud/oauth2/authorize |
| 令牌交换或刷新 | POST https://auth.acedata.cloud/oauth2/token |
| 撤销令牌 | POST https://auth.acedata.cloud/oauth2/revoke |
| 用户信息 | GET https://auth.acedata.cloud/api/v1/users/me |
| OAuth 应用管理 | https://auth.acedata.cloud/user/oauth-apps |
用 curl 拉取发现文档:
curl https://auth.acedata.cloud/.well-known/oauth-authorization-server
返回的 capabilities 包括:
response_type=codegrant_types=authorization_code, refresh_tokenS256 和 plain 两种 challenge 方法client_secret_post(机密客户端)和 none(公开 PKCE 客户端)Scope 是 OAuth 比手动复制 API Key 更安全的核心原因——用户能看到你申请了哪些权限,令牌也只限于这些权限范围内操作。
身份相关的 scope:
openid:返回用户的唯一 idprofile:返回 username、nickname、avatar、is_verified、date_joined 等字段email:返回 emailphone:返回 phone 和 region平台资源相关的 scope:
applications:read 和 applications:writecredentials:read 和 credentials:writeusage:readorders:read 和 orders:write还有一些聚合 scope,用起来更省事:
platform:read 等价于 applications:read credentials:read usage:read orders:readplatform:write 等价于 applications:write credentials:write orders:writeplatform 包含读写两组简单登录请求 openid profile 就够。如果是要在 IDE 或 MCP 客户端里配置凭证,请求 openid profile credentials:read credentials:write。只有需要 refresh token 时才加 offline_access。
到管理页面创建应用:
https://auth.acedata.cloud/user/oauth-apps
注册时选客户端类型:
Confidential(机密客户端):后端服务,可以安全存储 client_secretPublic(公开客户端):前端、桌面端、CLI、移动端等无法安全存储密钥的场景,必须用 PKCE还要配置 Redirect URI,必须和你后续发请求时传的 redirect_uri 完全一致。保存后会拿到 client_id。机密客户端还会显示一次 client_secret,看完就消失,立刻记下来。
浏览器重定向到这个地址:
https://auth.acedata.cloud/oauth2/authorize
一个典型的授权 URL 长这样:
https://auth.acedata.cloud/oauth2/authorize ?response_type=code &client_id=<your client_id> &redirect_uri=<your registered callback address> &scope=openid%20profile%20credentials:read &state=<random CSRF protection string> &code_challenge=<PKCE challenge value> &code_challenge_method=S256
state 参数一定要验证,这是你的 CSRF 保护。
PKCE 部分:先生成随机的 code_verifier,然后计算:
code_challenge = BASE64URL(SHA256(code_verifier))
把 code_challenge 放进授权 URL,把 code_verifier 留着,等下换令牌要用。用户授权后,浏览器会跳回:
<redirect_uri>?code=<authorization code>&state=<state returned as is>
如果用户拒绝,返回 error=access_denied 和一段 error_description。
授权码有效期只有 10 分钟,而且只能用一次,拿到就赶紧换令牌。
机密客户端用 client_secret 换:
curl -X POST https://auth.acedata.cloud/oauth2/token \ -d grant_type=authorization_code \ -d code=<code obtained in the previous step> \ -d client_id=<your client_id> \ -d client_secret=<your client_secret> \ -d redirect_uri=<callback address that exactly matches Step 2>
公开 PKCE 客户端用 code_verifier 换:
curl -X POST https://auth.acedata.cloud/oauth2/token \ -d grant_type=authorization_code \ -d code=<code> \ -d client_id=<your client_id> \ -d code_verifier=<code_verifier generated earlier> \ -d redirect_uri=<callback address>
成功返回的格式:
{ "access_token": "<JWT>", "token_type": "Bearer", "expires_in": 1296000, "scope": "openid profile credentials:read", "refresh_token": "<JWT, only when offline_access>"
}
access token 是 JWT(JSON Web Token),带 scope 声明,有效期 15 天。refresh token 只有请求了 offline_access 才会返回,有效期 30 天。
读用户信息,把 token 放在 Authorization: Bearer 请求头里:
curl https://auth.acedata.cloud/api/v1/users/me \ -H "Authorization: Bearer <access_token>"
返回哪些字段取决于用户授权了哪些 identity scope。
调平台资源 API 访问 api.acedata.cloud,同样带 bearer token。比如用户授权了 credentials:read:
curl https://api.acedata.cloud/api/v1/credentials/ \ -H "Authorization: Bearer <access_token>"
平台后端会校验 JWT 里的 scope 范围。如果 token 想访问未授权的资源,API 返回 403。
如果请求了 offline_access,access token 过期后可以刷新:
curl -X POST https://auth.acedata.cloud/oauth2/token \ -d grant_type=refresh_token \ -d refresh_token=<your refresh_token>
注意 refresh token 是滚动更新的,每次刷新后会给你一个新的,旧的就失效了,记得存最新的那个。
要撤销任意令牌:
curl -X POST https://auth.acedata.cloud/oauth2/revoke \ -d token=<access_token or refresh_token>
OAuth 错误统一格式:
{ "error": "<code>", "error_description": "<human-readable explanation>"
}
常见错误码:invalid_request、invalid_client、invalid_grant、access_denied、unsupported_grant_type。
实际集成中最常踩的是 invalid_grant:授权码过期了(10分钟窗口)、授权码被重复使用了、PKCE 校验失败、或者 redirect_uri 不匹配。
几个有用的限制:每个账户最多创建 20 个 OAuth 应用,授权码 10 分钟有效且只能用一次,access token 有效期 15 天,refresh token 有效期 30 天且每次刷新滚动更新,redirect_uri 必须完全匹配。
接这个流程主要就是为了让用户在登录时有个熟悉的体验,同时保持权限范围可控、可审计。如果你在做 Agent、MCP 客户端或者内部面板,这套方案直接能用。