タイトル: Bad Authentication data.
SEOタイトル: Twitter (X) API「Bad Authentication data」原因と対処|OAuth 認証エラー
| この記事の要点 |
|
エラー内容
Twitter (X) API へのリクエストで以下のような JSON レスポンスが返る:
{
"errors": [
{
"code": 215,
"message": "Bad Authentication data."
}
]
}
HTTP ステータスは 400 Bad Request が一般的。code 215 が「Bad Authentication data」の固有番号です。
原因
Twitter API は OAuth 1.0a または OAuth 2.0 でリクエストを認証します。このエラーは認証情報まわりの広い問題を指すため、複数の原因があります:
- API キー / シークレットが間違っている(タイポ・別アプリのキーと混同)
- アクセストークンが期限切れまたは失効済み
- OAuth 署名の計算ミス(パラメータの並び・URL エンコード)
- タイムスタンプのズレ(サーバ時刻が大幅に違う)
- Nonce の重複(連続呼び出しで同じ nonce 使用)
- API バージョン違い(v1.1 エンドポイントに v2 認証で叩く等)
- Authorization ヘッダの欠落(書き換えで消えた)
- 有料 tier 必須エンドポイントを無料 tier で叩いている
対処1: キーを再生成
- Developer Portal にログイン
- 対象アプリの "Keys and tokens" タブを開く
- API Key and Secret を「Regenerate」
- Access Token and Secret も「Regenerate」
- 新しい値をコードまたは環境変数に反映
- キャッシュをクリアして再実行
対処2: OAuth 1.0a 署名の検証
署名計算は仕様が厳密。以下を確認:
1. パラメータをアルファベット順にソートしているか
2. URL エンコード(RFC 3986: ! * ' ( ) を含む)が正しいか
3. signature_base_string の構造:
HTTP_METHOD & encoded_URL & encoded_parameters
4. signing_key の構造:
consumer_secret & oauth_token_secret
5. HMAC-SHA1 で署名し、Base64 エンコード
PHP のサンプル(最も多いハマりどころ)
function buildSignature($method, $url, $params, $consumerSecret, $tokenSecret) {
// ① パラメータをアルファベット順
ksort($params);
// ② URL エンコード(PHP の urlencode は RFC 3986 と微妙に違う)
$encodedParams = [];
foreach ($params as $k => $v) {
$encodedParams[] = rawurlencode($k) . '=' . rawurlencode($v);
}
$paramString = implode('&', $encodedParams);
// ③ signature base string
$baseString = strtoupper($method) . '&'
. rawurlencode($url) . '&'
. rawurlencode($paramString);
// ④ signing key
$signingKey = rawurlencode($consumerSecret) . '&' . rawurlencode($tokenSecret);
// ⑤ HMAC-SHA1 + Base64
return base64_encode(hash_hmac('sha1', $baseString, $signingKey, true));
}
// よくあるミス:
// - urlencode() (RFC 1738) ではなく rawurlencode() (RFC 3986) を使う
// - パラメータに oauth_signature 自体を含めない
// - クエリ文字列とボディ POST パラメータの両方を署名対象に含める
対処3: ライブラリ使用(推奨)
OAuth 署名を自前実装すると署名ミスでハマりがち。専用ライブラリを使うのが安全:
// composer require abraham/twitteroauth
use Abraham\TwitterOAuth\TwitterOAuth;
$connection = new TwitterOAuth(
$consumerKey,
$consumerSecret,
$accessToken,
$accessTokenSecret
);
// v1.1 API 呼び出し
$user = $connection->get("account/verify_credentials");
// v2 API 呼び出し
$connection->setApiVersion('2');
$tweet = $connection->post("tweets", ["text" => "Hello"]);
if ($connection->getLastHttpCode() === 200) {
// 成功
} else {
print_r($connection->getLastBody()); // エラー詳細
}
対処4: API バージョンの確認
| バージョン | エンドポイント | 認証 | 2024 年現在 |
|---|---|---|---|
| v1.1 | api.twitter.com/1.1/... | OAuth 1.0a | 多くが廃止 / 有料化 |
| v2 | api.twitter.com/2/... | OAuth 2.0 Bearer / OAuth 1.0a | 現行 |
対処5: タイムスタンプ確認
# サーバの時刻がずれている可能性
date
# NTP で同期
sudo ntpdate -s pool.ntp.org
# Docker コンテナ内なら
docker exec myapp date
# 大きくずれていたら、ホストの時計同期 / コンテナ再作成
X API 有料化の影響(2023 年以降)
- Free tier: 投稿のみ可、月 1,500 ツイート読み取り
- Basic ($100/月): 投稿 + 限定的な読み取り
- Pro ($5,000/月): 本格的な API 利用
- 無料 tier でアクセスできないエンドポイントを叩くと 403 + Bad Authentication 系エラーが出る
関連エラー
- code 32: Could not authenticate you — キー不正・ヘッダ不足
- code 89: Invalid or expired token — アクセストークンが無効
- code 187: duplicate — 同じ内容を連投
- code 88: Rate limit exceeded — レート制限超過
- code 326: account is locked — アカウント凍結
デバッグ Tips
- curl で素のリクエストを送ってレスポンス全文を確認
- nginx / Tomcat のアクセスログでリクエストヘッダを確認
- ngrok 経由でリクエストを観測してヘッダを目視確認
- Postman の OAuth 1.0a 設定ウィザードで動作する設定を見つける → コードに移植