修复:`xurl` OAuth 2.0 在 X API 上因 'unauthorized_client' 失败
Source: Dev.to
如果你正在使用 xurl 对 X API 进行身份验证,并且看到以下错误:
OAuth2 authentication failed: Auth Error: TokenExchangeError
(cause: oauth2: "unauthorized_client" "Missing valid authorization header")解决办法只需在 X 开发者门户中进行一次设置。
为什么会发生
xurl 使用 OAuth 2.0 PKCE 流程,该流程面向 公共客户端(移动应用、CLI、SPA)。公共客户端在令牌交换时在请求体中发送凭证。
X API 应用默认被创建为 机密客户端。机密客户端必须通过 Authorization: Basic 头部发送凭证——而 xurl 并未使用此机制。当 xurl 在没有该头部的情况下发送令牌交换请求时,X 会以 unauthorized_client 拒绝。
您可以通过对客户端 ID 进行 base64 解码来确认应用类型:
echo "YOUR_CLIENT_ID" | base64 -d- 如果解码后的值以
:ci结尾,则为机密客户端。 - 如果以
:na结尾,则为本地(公共)客户端。
修复方案
将您的 X 应用类型从 Web App 更改为 Native App,在开发者门户中。
在门户中:
选择您的应用 → User authentication settings → Edit- 将 App type 设置为 Native App
- 将 Callback URI 设置为
http://localhost:8080/callback - 保存
重新注册全新凭证 并使用
xurl进行身份验证:# 在门户中重新生成凭证后,使用新凭证重新注册 xurl auth apps add my-app \ --client-id YOUR_NEW_CLIENT_ID \ --client-secret YOUR_NEW_CLIENT_SECRET # 存储 bearer token 和 OAuth 1.0a 凭证(可选) xurl auth app --bearer-token YOUR_BEARER_TOKEN xurl auth oauth1 \ --consumer-key YOUR_CONSUMER_KEY \ --consumer-secret YOUR_CONSUMER_SECRET \ --access-token YOUR_ACCESS_TOKEN \ --token-secret YOUR_ACCESS_TOKEN_SECRET # 设置为默认应用 xurl auth default my-app # 运行 PKCE 流程 xurl --app my-app auth oauth2完成基于浏览器的授权流程后,验证一切是否正常:
xurl auth status xurl --auth oauth2 /2/users/me # 用户上下文 xurl --auth oauth1 /2/users/me # 同样可用 xurl --auth app "/2/tweets/search/recent?query=hello&max_results=5" # 应用专用
额外:更改应用类型后重新生成凭证
当您切换应用类型时,X 会颁发一个带有 :na 后缀的新 Client ID。旧的 :ci ID 在 PKCE 流程中将失效,因此请在使用 xurl 重新注册之前,从 Keys and Tokens 选项卡复制新的值。
TL;DR
| 应用类型 | 客户端 ID 后缀 | 是否兼容 xurl OAuth 2.0? |
|---|---|---|
| Web 应用 / Bot | :ci(机密) | ❌ |
| 本地应用 / SPA | :na(公开) | ✅ |
将 X 开发者门户中的 Native App 更改为本地应用,重新生成凭据,xurl 的 OAuth 2.0 流程即可正常工作。
Web 应用程序支持 OAuth 2.0 吗?
Web App/Bot 应用确实支持 OAuth 2.0,但不是通过 xurl,因为 xurl 只实现了公共客户端的 PKCE 流程。机密客户端在令牌交换步骤需要 Authorization: Basic 头,而大多数 CLI 工具(包括 xurl)都不处理 X 的这种情况。
使用 curl 手动进行 OAuth 2.0
步骤 1 – 构建授权 URL 并在浏览器中打开:
# Generate a code verifier + challenge (PKCE is optional but recommended)
VERIFIER=$(openssl rand -base64 32 | tr -d '=+/' | cut -c1-43)
CHALLENGE=$(echo -n "$VERIFIER" | openssl dgst -sha256 -binary | base64 | tr '+/' '-_' | tr -d '=')
STATE=$(openssl rand -hex 16)
CLIENT_ID="YOUR_CLIENT_ID"
REDIRECT="http://localhost:8080/callback"
echo "https://x.com/i/oauth2/authorize?response_type=code&client_id=${CLIENT_ID}&redirect_uri=${REDIRECT}&scope=tweet.read%20users.read%20offline.access&state=${STATE}&code_challenge=${CHALLENGE}&code_challenge_method=S256"在浏览器中打开打印出的 URL,进行授权,然后从重定向 URL 中复制 code=… 的值。
步骤 2 – 使用 Basic Auth 交换代码:
CLIENT_ID="YOUR_CLIENT_ID"
CLIENT_SECRET="YOUR_CLIENT_SECRET"
CODE="CODE_FROM_REDIRECT"
VERIFIER="THE_VERIFIER_FROM_STEP_1"
curl -X POST https://api.x.com/2/oauth2/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "Authorization: Basic $(echo -n "${CLIENT_ID}:${CLIENT_SECRET}" | base64 -w 0)" \
-d "grant_type=authorization_code&code=${CODE}&redirect_uri=${REDIRECT}&code_verifier=${VERIFIER}"响应中包含一个 access_token,可以直接在 xurl 中使用:
xurl -H "Authorization: Bearer ACCESS_TOKEN" /2/users/me何时使用哪种方式
| 使用场景 | 应用类型 | 认证方式 |
|---|---|---|
| CLI / 本地开发 | Native App | xurl oauth2 (PKCE, no secret) |
| 服务器端应用 | Web App / Bot | OAuth 2.0 confidential (Basic Auth, secret stays on server) |
| 以自己身份发布 | Any | OAuth 1.0a (simplest for personal use) |
| 只读公共数据 | Any | Bearer token (app‑only) |
Web App/Bot + OAuth 2.0 旨在用于服务器端应用程序,客户端密钥永不离开您的服务器。对于本地 CLI 使用,Native App 是正确的选择。OAuth 1.0a 适用于任何应用类型,通常是个人或开发使用时阻力最小的方案。