Yii2のauthclientでTwitterクライアントを使ってみたところ、OAuth認証が成功した後、GET系のTwitter REST APIsは普通に動くが、POST系が全て401エラーを返す という問題に遭遇しました。
例えば
$client->api('friendships/lookup.json', 'GET', ['screen_name' => 'kamicup']);
はふつーに動くけど、
$client->api('friendships/create.json', 'POST', ['screen_name' => 'kamicup']);
などは
{"errors":[{"message":"Could not authenticate you","code":32}]}
というエラーレスポンスが返ってきます。
で、authclientのソースを追って調べましたが、
結論から言うと、
yii\authclient\clients\Twitter は(今のところ)POST系のTwitter REST APIsが動くようにはなっていません。POST系を使うには下記の回避策が必要になります。
#回避策
親クラスである yii\authclient\OAuth1 の処理を一部書き換えてやる必要があります。
具体的には、ここです。
protected function composeRequestCurlOptions($method, $url, array $params) {
...
$curlOptions[CURLOPT_POSTFIELDS] = $params;
...
$curlOptions[CURLOPT_HTTPHEADER] = ['Content-Type: application/atom+xml', $authorizationHeader];
OAuth認証処理はこれでいいのですが、Twitter REST APIsのPOSTメソッドを呼ぶ時には Content-Typeをapplication/x-www-form-urlencodedにしてやる必要があります。
また、curl_setopt()関数はCURLOPT_POSTFIELDSオプションに配列を渡すと強制的にmultipart/form-dataになる仕様なので、配列ではなく文字列を渡してやる必要があります。
これを踏まえて、使えるようにしたものを↓に置いておくので、yii\authclient\clients\Twitter の代わりにどうぞ。
<?php
use yii\authclient\OAuth1;
class Twitter extends OAuth1
{
public $authUrl = 'https://api.twitter.com/oauth/authorize';
public $requestTokenUrl = 'https://api.twitter.com/oauth/request_token';
public $requestTokenMethod = 'POST';
public $accessTokenUrl = 'https://api.twitter.com/oauth/access_token';
public $accessTokenMethod = 'POST';
public $apiBaseUrl = 'https://api.twitter.com/1.1';
protected function initUserAttributes() {
return $this->api('account/verify_credentials.json', 'GET');
}
protected function defaultName() {
return 'twitter';
}
protected function defaultTitle() {
return 'Twitter';
}
protected function composeRequestCurlOptions($method, $url, array $params) {
if ( $method === 'POST' && strpos($url, $this->apiBaseUrl) === 0 ) {
$curlOptions = [];
$curlOptions[CURLOPT_POST] = true;
if (!empty($params)) {
$curlOptions[CURLOPT_POSTFIELDS] = http_build_query($params);
}
$authorizationHeader = $this->composeAuthorizationHeader($params);
if (!empty($authorizationHeader)) {
$curlOptions[CURLOPT_HTTPHEADER] = ['Content-Type: application/x-www-form-urlencoded', $authorizationHeader];
}
return $curlOptions;
} else {
return parent::composeRequestCurlOptions($method, $url, $params);
}
}
}