fix(oauth): token 兑换改用 HTTP Basic(client_secret_basic),修 401
Cloudflare OAuth 客户端默认 token_endpoint_auth_method = client_secret_basic, 只接受 Authorization: Basic 头。之前把 client_id/secret 放在 POST body (client_secret_post),token 端点直接 401 invalid_client,描述里点名 "method 'client_secret_post' was requested" —— 表现为 Cloudflare 授权成功、 回调却报 token exchange failed: 401。 - exchangeToken 改 Basic 认证,凭据按 RFC 6749 §2.3.1 先 form-urlencoded 再 base64 - 失败时带回 Cloudflare 原始 error/error_description;invalid_client 额外提示 「secret 只在创建时显示一次,轮换后旧值失效」 - cf-mock 复刻「只认 Basic」的行为,没有 Basic 头就返回 401(旧代码会被打回) - 断言 token body 不含 client_id/client_secret,Basic 头解出正确凭据 - 88/88 测试
This commit is contained in:
+15
-1
@@ -13,7 +13,7 @@ export interface CfCall {
|
||||
url: string;
|
||||
/** 路径(去掉 base) **/
|
||||
path: string;
|
||||
/** Authorization 头(OAuth Bearer token) **/
|
||||
/** Authorization 头(Cloudflare API 是 OAuth Bearer token;token 端点是 Basic) **/
|
||||
authorization: string | null;
|
||||
/** 请求体文本(URL 编码或 JSON);multipart 上传记 null **/
|
||||
body: string | null;
|
||||
@@ -122,6 +122,20 @@ export function installCloudflareMock(opts: CfMockOptions = {}): CfMock {
|
||||
return new Response(null, { status: 302, headers: { Location: "https://dash.cloudflare.com/login" } });
|
||||
}
|
||||
if (url === TOKEN_URL) {
|
||||
// 复刻 Cloudflare 的客户端认证要求:只接受 HTTP Basic(client_secret_basic)。
|
||||
// 把 client_id/secret 放进 POST body(client_secret_post)会被 401 invalid_client 拒绝,
|
||||
// 且错误描述会点名 "method 'client_secret_post' was requested"(2026-09-30 线上踩过)。
|
||||
const auth = headers.get("authorization") ?? "";
|
||||
if (!auth.startsWith("Basic ")) {
|
||||
const desc =
|
||||
"Client authentication failed (e.g., unknown client, no client authentication included, " +
|
||||
"or unsupported authentication method). The OAuth 2.0 Client supports client authentication " +
|
||||
"method 'client_secret_basic', but method 'client_secret_post' was requested.";
|
||||
return new Response(JSON.stringify({ error: "invalid_client", error_description: desc }), {
|
||||
status: 401,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
if ((opts.tokenStatus ?? 200) !== 200) {
|
||||
return new Response(JSON.stringify({ error: "invalid_grant" }), { status: opts.tokenStatus });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user