WordPress JSON REST API に OAuth でログインしてみた
Posted by admin at 8:35 日時 2014/09/25
まとめ:割と大変(今のところ)。
WordPress JSON REST APIとは
WordPress JSON REST APIとは、WordPressの外部からWordPressのデータベースにアクセスできるAPIのことです。まだWordPressに組み込まれた機能ではありませんが、すでに注目を集めている新機能のひとつです。バージョン4.1のマイルストーンに組み込まれていますので、近い将来にコアに組み込まれると思われます。他の新機能と同様プラグインで開発が進められています。
このAPIを使うと、WordPressとやりとりできるリソースはURIで表され、JSON形式のデータでやりとりすることができます。RESTの概念を説明しだすと長くなるのですが、要するにかんたんにシステムとデータのやり取りをするための人気のある規格と覚えておいていただければと思います。実際に、これまでもXML-RPC APIでデータのやりとりは可能であったので、JSON REST APIはそれより簡便で汎用的であることから注目されているのだと思います。
参考URL
WordPress JSON REST APIでできること
JSON REST APIでアクセスできるリソースは公式サイトに一覧されています。投稿の取得/作成/編集/削除、カスタムフィールドの取得/作成/編集/削除、添付ファイルの取得/作成、ユーザーの取得/作成/編集/削除、タクソノミーの取得など、外観やプラグインに関する部分以外はの範囲を管理画面にアクセスせずにコントロールが可能になります。WordPressのデータに外部からアクセスできると、例えば次のような活用方法が考えられます。
- Webサイト全体のCMSとしてはconcrete5を使用し、ブログはWordPressを使用する。WordPressから情報を取得しconcrete5サイトに表示する(とりあえず個人的に一番やりたいこと)。
- WordPressを「記事を管理するデータベース」とみなし、表示や記事の投稿/編集を行なうインターフェースを完全にWordPressから置き換えてしまう。
- 外部のフォームから送信されたレビューデータをWordPress内の「お客様の声」カテゴリに下書き状態で自動的に保存。
- 不動産サイトをWordPressで構築している場合に、社内データベースと連携し物件に売約がついたら即座にカスタムフィールドに売約済みフラグをつける。
- WordPressサイトはそのままで、特定のデータを取り出して使うモバイルアプリを開発する
この辺のカスタマイズが欲しい方は弊社までお問い合わせください
OAuthとは
WordPress REST APIを介すると外部から管理画面にアクセスせずに様々な情報をやりとりできるわけですが、当然ですが誰でもこのAPIが使えると困ります。なので、WordPressのログイン画面を使わずに、特定の外部のアプリケーションからWordPressにアクセスすることを認証する仕組みが必要になります。OAuthは非常に広範に使われているAPI認証のための規格で、Twitter、Facebook、GitHubなど様々なサービスのAPIの認証に使われています。WordPress REST APIはOAuth1.0a(Three Legged)を採用しています。なぜこれから出てくるAPIで1.0なのかは謎ですが…まだ2.0は時期尚早ということなのでしょうか(※Facebookのコメントで、2.0は暗号化必須だからではないか、とのこと)。
参考URL
OAuthの実装は複雑なので、自力で実装するのはおすすめしません。汎用的な規格なので、世の中には色々なOAuth用のライブラリがあります。今回はconcrete5からWordPress REST APIへのアクセスを目指していますので、concrete5も使用しているZend Framework1を使用することにします。余談ですが、Symfonyで使えるOAuthライブラリは結構あるので、Symfonyを採用するconcrete5.7で使えるようにいずれはSymfonyで使えるライブラリを使って書き直そうと思っています。
それでは、実装して行きましょう
WordPressにプラグインをインストール
必要なのはJSON REST APIプラグインとOAuth Serverプラグインの2つです。この2つは将来的にコアに統合される予定みたいです。現在開発中の機能なので、今のところはGitHubから最新を落としてくるのがおすすめです。
Consumer KeyとConsumer Secretの発行
OAuthでログインするには、まずログインされる側、ここではWordPressの方で専用のキーを発行する必要があります。キーの発行はUIでも行なえるようになるみたいですが開発中で、今のところコマンドラインでしか行なえません。
$ wp oauth1 add ID: 4 Key: sDc51JgH2mFu Secret: LnUdIsyhPFnURkatekRIAUfYV7nmP4iF3AVxkS5PRHPXxgOW
コマンドライン(cli)の使い方まで解説すると長くなるので、こちらのブログをみていただければざっと分かると思います。
第32回WordBench大阪に参加して〜WP-CLIの情報だよ〜 – Kimiya Kitaniの徒然なるブログ
発行したコンシューマーキーは、適当に画面を作って保存しておけば良いですね。
Request Tokenの取得
発行されたConsumer KeyとConsumer Secretをもとに、Request Tokenを取得します。ログインしたい側からWordPressへのリクエストになります。ここでは、Zend_OAuthを使っています。
Introduction to OAuth – Zend_Oauth – Zend Framework
// 設定 $config = array( 'requestScheme' => Zend_Oauth::REQUEST_SCHEME_HEADER, 'callbackUrl' => $callback_url, 'requestTokenUrl' => $request_token_url, 'userAuthorizationUrl' => $authorization_url, 'accessTokenUrl' => $access_token_url, 'consumerKey' => $consumer_key, 'consumerSecret' => $consumer_secret ); // コンシューマーの生成 $consumer = new Zend_Oauth_Consumer($config); // リクエストトークンを取得 $token = $consumer->getRequestToken(); // トークンをセッションに一時保管 $_SESSION['REQUEST_TOKEN'] = serialize($token);
$callback_url
はWordPress側での認証が終わったら戻ってくるURLの指定です。
$request_token_url
と$authorization_url
、$access_token_url
の3つはハードコードせず、あらかじめWordPress JSON APIから取得しておく必要があります(これらの情報の取得に認証は要りません)。2つのプラグインがインストールされた状態で/wp-json
にアクセスすると、WordPressサイトの基本情報が表示されますが、この中の authentication
ノード内に3つのURLが表示されています。
$consumer_key
と$consumer_secret
はあらかじめ発行しておいたものです。
このあとのステップでWordPress側の画面に遷移して承認するステップがあるのですが、その際画面を離れます。取得したリクエストトークンは、一時的に保管しておく必要があります。ここではセッションを使用していますが、データベースでも構いません。
認証画面にリダイレクト
次に、取得したリクエストトークンをもとにWordPressの認証画面にリダイレクトさせます。このとき、システムに戻ってくるURLをGETパラメーターで付与しておく必要があります。今後WordPress側でコンシューマーキーの管理画面ができた際に、コールバックURLも指定できるようになる可能性はありますが、とりあえず現状では指定が必要です。
$consumer->redirect(array( 'oauth_callback' => $callback_url ));
もうひとつポイントなのですが、このコールバックURLはWordPress側のフィルターで拒否されてしまいます(めんどくせえ)。WordPress側に適当なプラグインを作って、戻ってくるURLのホストを許可しておくとよいでしょう。
<?php /* Plugin Name: Hacks */ add_filter('http_request_host_is_external',function($allow, $host, $url) { if ($host = 'c5631.example.com') $allow = true; return $allow; },10,3);
こんな感じで適当に認証ボタンを作りました。クリックするとWordPressに飛んで認証を求められます。ここで認証したユーザー権限がREST APIで取得できるデータの権限に該当しますので、adminで認証すると全データが操作可能になります。
Access Tokenの取得
認証ボタンをクリックすると認証済みのトークンがコールバックURLに渡されてきます。この認証済みのトークンを使ってアクセストークンを取得します。アクセストークンを一度取得すると、毎回WordPressの認証画面を踏む必要なく、APIへのアクセスを行なうことができます。このステップも詳細は省略。ライブラリにお任せ。
// 設定 $config = array( 'requestScheme' => Zend_Oauth::REQUEST_SCHEME_HEADER, 'callbackUrl' => $callback_url, 'requestTokenUrl' => $request_token_url, 'userAuthorizationUrl' => $authorization_url, 'accessTokenUrl' => $access_token_url, 'consumerKey' => $consumer_key, 'consumerSecret' => $consumer_secret ); // コンシューマーの生成 $consumer = new Zend_Oauth_Consumer($config); // アクセストークンを取得 $token = $consumer->getAccessToken($_GET, unserialize($_SESSION['REQUEST_TOKEN'])); // Zend_Oauth_Token_AccessオブジェクトからTokenとSecretを分けて取り出しておきます $oauth_token = $token->getToken(); $oauth_token_secret = $token->getTokenSecret(); // 適当に保存します $co->save('WP_REST_API_OAUTH_TOKEN', $oauth_token); $co->save('WP_REST_API_OAUTH_TOKEN_SECRET', $oauth_token_secret); // 一時保管していたリクエストトークンを破棄します $_SESSION['REQUEST_TOKEN'] = null;
WordPress REST APIに接続
以上で認証が完了しましたので、晴れてWordPress REST APIに接続できます。長かった…。
// アクセストークンとコンシューマーキーの両方が必要です $token = new Zend_Oauth_Token_Access; $token->setParams(array( Zend_Oauth_Token_Access::TOKEN_PARAM_KEY => $oauth_token, Zend_Oauth_Token_Access::TOKEN_SECRET_PARAM_KEY => $oauth_token_secret )); $client = $token->getHttpClient(array( 'consumerKey' => $consumer_key, 'consumerSecret' => $consumer_secret )); // ユーザーを取得してテストしてみます(認証が必要)。 $client->setUri($wp_rest_api_url.'/users'); $client->setMethod(Zend_Http_Client::GET); $response = $client->request(); // データはJSON形式で送られてきますので、デコードしてデータが取り出せれば成功! $data = Zend_Json::decode($response->getBody());
ただ、ここでもちょっとしたトラップが発動。CGIモードで動いている場合らしいのですが、PHPにAuthenticateヘッダのデータが渡らないことがある模様。そのため、WordPress側の.htaccessとindex.phpにちょっと細工して回避。
How can I access request headers that don’t appear in $_SERVER?
.htaccess
# BEGIN WordPress <IfModule mod_rewrite.c> RewriteEngine On RewriteBase / RewriteRule ^index\.php$ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.php [L] </IfModule> # END WordPress
index.php
<?php // ここから追加 if (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) { $_SERVER['HTTP_AUTHORIZATION'] = $_SERVER['REDIRECT_HTTP_AUTHORIZATION']; } // ここまで追加 /** * Front to the WordPress application. This file doesn't do anything, but loads * wp-blog-header.php which does and tells WordPress to load the theme. * * @package WordPress */ /** * Tells WordPress to load the WordPress theme and output it. * * @var bool */ define('WP_USE_THEMES', true); /** Loads the WordPress Environment and Template */ require( dirname( __FILE__ ) . '/wp-blog-header.php' );
index.phpの方は、面倒くさがらずにプラグインにしようかと思いましたがやっぱり面倒になったのでとりあえずこれで。