WP REST API の OAuth 認証の方法と何が起こっているのかとなぜそんなことをしているのか

OAuth認証やってまいります。

WP REST API の OAuth 認証用のプラグインの github レポジトリのドキュメントのフローの通り進めます。また、リクエストの送信には、Paw という Mac アプリを利用します。

この記事自体の中に上記のドキュメントで伝えられていることを逐次翻訳して残しておこうと思います。上記のドキュメントは読まなくても大丈夫になるように頑張ります。間違いがあったら、 @shinichiN へお願いします。

STEP 0: Client Key と Client Secret を作成する

ドキュメントサイトの方には「OAuth のプラグインにUIはありません。そのうち作ります」などと書かれておりましたが、実際にはありました。

WP REST API の OAuth プラグインのアプリケーション作成画面
WP REST API の OAuth プラグインが提供するアプリケーション作成画面

wp-cli を利用して $ wp oauth1 add みたいなことをしないといけない、とドキュメントサイトにはありますが、管理画面でできました。

というわけでさっそく作っていきましょう。

コンシューマを作ってみました。
コンシューマを作ってみました。

NameDescription は管理用のものだと思われますので適当に入れます。Callback URL はよく分からないですが必須なので /success としました。

Client Key と Client Secret をメモしておきましょう。

OAuth フローの概要と、その意義

OAuth1/Auth-Flow.md at master · WP-API/OAuth1 にある OAuth 認証のフローを翻訳します。クライアント側から見ると、3つのフローに分かれています。

  1. Temporary Credentials Acquisition: The client gets a set of temporary credentials from the server.
  2. Authorization: The user “authorizes” the request token to access their account.
  3. Token Exchange: The client exchanges the short-lived temporary credentials for a long-lived token.

翻訳(と若干の色々を追加)は、

  1. 一時的な証明書の取得: サーバから、一時的な証明書を受け取る(この証明書のことを一般的にリクエストトークンと呼ぶ)
  2. 認証: ユーザー(人間)が、リクエストトークンを認証してアカウントにアクセスする
  3. トークンの交換: クライアントが一時的な証明書を、長期的な証明書と交換する
    となります。(交換後の証明書のことを、一般的にアクセストークンと呼ぶ)

となります。

OAuth 認証の意義は、以下の様なことと理解するとよいと思います。

まず、WordPress のサイトが有り、OAuth プロバイダとして動くことができます。今、やろうとしていることは、WordPress の REST API を利用して認証済みのアプリケーションでなければ行えないことを実行したい、ということです。

ログインしていないとできないこととはたとえば、投稿のメタデータを取得するとか、データをアップデートする、といったことです。WP REST API では、ログインしていないユーザーであっても公開情報を取得することは可能ですが、内部的なデータは取得できませんし、データを追加したり変更したり削除したりといったことももちろんできません。

外部のアプリケーション(スマホアプリやウェブアプリ、デスクトップアプリなど)から、こうしたことを行いたい場合に、最初に思いつくのは毎回ユーザー名とパスワードを送ってログインさせればいいじゃないか、ということですが、もちろんこれは大問題となります。

  • 毎回パスワードを送るのがまずい。
  • 外部のアプリケーションにユーザー名とパスワードを教えてあげないといけないのがまずい。

そこで OAuth の認証の仕組みが必要になるわけですね。3つの複雑なフローを経る中で、一度、WordPress サイトにログインできる人間が、外部のアプリケーションに対して一度許可を与え、許可を与えられたアプリケーションが上記で述べたようなログインユーザーにしかできない処理を行えるようにする、という仕組みです。

賢いですねぇ。誰が考えたんだろう。この話は、高橋文樹さんも書いてくれていますので読んでみてください。

WP REST APIを利用したNodeJSのWebアプリを死ぬ思いで作る

OAuth 認証で使う3つの エンドポイント URL

さて、上記の3つのフローですが、それぞれのステップに対応するエンドポイントURL があります。これらは WP REST API のプラグインが用意してくれるものです。

これらのエンドポイントURL は、 /wp-json にアクセスするとご案内があります。

/wp-json にアクセスした時のレスポンス
/wp-json にアクセスした時のレスポンス
  1. $.authentication.oauth1.request : リクエストトークンをもらうためのエンドポイント
  2. $.authentication.oauth1.authorize :認証用のエンドポイント
  3. $.authentication.oauth1.access :アクセストークン取得用のエンドポイント

これから行なう3つのフローの中で、上記のURL にリクエストを送り、新しい情報をもらったり、ログインと認証を行うなどの作業をしていくことになります。

では、OAuth 認証フローを始めます。

Step 1: Temporary Credentials Acquisition (リクエストトークンの取得)

まずは、一時的な証明書(リクエストトークン)を貰いに行きます。エンドポイント URL は /oauth1/request です。

このエンドポイントに対して、 Step 0 で取得してきた Client KeyClient Secret を使って作成した色々なものを組み合わせたリクエストを送ります。

下の画像は、Client KeyClient Secret を入力しているところです。

WordPress 側で作成した consumer key と consumer secret を入力して、 /oauth1/request に送信
WordPress 側で作成した consumer key と consumer secret を入力して、 /oauth1/request に送信。oauth_token、oath_token_secret、oauth_callback_confirmed(=true) がレスポンスされた

/oauth1/request に送信する際のリクエストヘッダーは以下のようになりました。

GET /oauth1/request HTTP/1.1
Authorization: OAuth oauth_consumer_key="fbQ9KPUmdDLP", oauth_nonce="4Lxk40ujU5KQU7j53E06dKGlLwQkKm1W", oauth_signature="Ra%2BrRiy2tLKk9ub0qMABW4g57gQ%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1451907555", oauth_version="1.0"
Host: wp-api.dev
Connection: close
User-Agent: Paw/2.2.7 (Macintosh; OS X/10.9.5) GCDHTTPRequest

2行目の Authorization: から始まる欄にいろいろな key=value 型のデータが入っています。

  • oauth_consumer_key は WordPress の管理画面から取得した Client Key のことです。
  • oauth_nonce は Paw が勝手に入れてくれています。noncenumber used once のことです。OAuth プロバイダ(ここではWordPress)が同じリクエストを2回以上処理しないようにするために送ります。
  • oauth_timestamp も Paw が勝手に作ってくれます。これは、認証に時間制限を持たせるためではなく(もちろんそうすることもできるけど)、nonce やトークンをプロバイダが永遠に保持しておかないといけないのを防ぐためのもの
  • oauth_signature も Paw が作ってくれます。これは署名で、作り方がポイントで、ざっくり概要を掴むと、以下の作り方になります。
    • client key や タイムスタンプ、nonce などを組み合わせてある文字列を作成する
    • client keyclient secret を組み合わせた文字列を作成する
    • 上記の2つの文字列を、HMAC-SHA1 というメソッドでハッシュ化する(あと、base64 エンコードもする)

サーバ側(WordPress側)では、送信されてきたこれらのデータを使って同じことを繰り返すことで、データを送信してきたクライアント(ここでは Paw アプリ)が悪いやつじゃないかを確認します。client secret 自体は送信しておらず、でも署名の生成過程の中には含まれている、というところがポイントです。上記のリクエストヘッダーの中でも、client secret 自体は送信していませんね。Paw に入力したのは signature (署名)の生成のためなのでした。

サーバ側での確認が終わり、有効なクライアントであることがわかると、レスポンスを返してくれます。

oauth_tokenoath_token_secret (と oauth_callback_confirmed )が返ってきました。raw は以下です。

HTTP/1.1 200 OK
Date: Mon, 04 Jan 2016 10:00:56 GMT
Server: Apache
X-Powered-By: PHP/7.0.0
Content-Length: 134
Connection: close
Content-Type: application/x-www-form-urlencoded; charset=utf-8

oauth_token=CZpmXOtP98rsvrBk668QZZxE&oauth_token_secret=CTAdf4c1ZdLzoJJvC195VoMN3t9t3oPphK3sAbSceHYfUjwl&oauth_callback_confirmed=true

oauth_token と oath_token_secret がリクエストトークンと呼ばれる一時的な証明書(Temporary Credentials)です。

これが第一ステップでした。

Step 2: Authorize 認証

上記の oauth_token を使って認証のステップに進むことができます。

oauth_token を、2個目のエンドポイント URL である、 /oauth1/authorize/ に GET パラメータとして渡します。このステップでは、WordPressのログイン画面への転送や、人間による認証のプロセスがありますのでブラウザを使います。

つまり以下のURLにアクセスすることになります。

http://example.com/oauth1/authorize?oauth_token=<step1で取得してきたoauth_token>

このURLにブラウザでアクセスをすると、

  • 「このアプリ(Paw)を認証しますか?」という人間向けの画面に転送される
  • WordPress にログインをしていない場合には、その前にログイン画面に転送される
ログインした後に表示される認証の画面
ログインした後に表示される認証の画面

ここで、人間が登場してこのクライアントアプリに対して権限を与えることになります。ここで GET パラメータとして使われている oauth_tokenclient key/secret を知っていないと作れないことに留意ですね。

Authorize をクリックすると、以下のような画面になり、oauth_verifier という値を受け取ることができます(これは CSRF を防ぐためのもので次のステップで使います。)。

またこの時、WordPress側ではこの oauth_token が認証されたとマーキングされます。

ここまでで、Step 2は完了です。

Step 3: Token Exchange (トークンの交換)

最後のステップです。 verification token を利用して3つ目のエンドポイントでトークンを long-lived のものと交換します。

Step 1 で取得した oauth_token と oauth_token_secret はリクエストトークンと呼ばれる一時的なものでした、これを、もっと長い期間使うことができるものに差し替えてもらいます。そうすると同じ oauth_tokenoauth_token_secret ではあるもののアクセストークンと呼ばれるものになります。

交換が終わったら、アクセストークンを利用して交信を行うようになります。リクエストトークン、 verification token は破棄してOKになりますし、保持していても意味のないものになります。

Paw での操作はちょっと複雑になります。エンドポイント URL は /oauth1/access/ となります。一番下の Additional Params 欄に oauth_verifier=<step2で取得したoauth_verifier> を追加して POST 送信します。

一番下に追加した oauth_verifier を追加して /oauth1/access/ にリクエストすると oauth_token と oath_token_secret がもらえる
一番下に oauth_verifier を追加して /oauth1/access/ にリクエストすると新しい oauth_token と oath_token_secret がもらえる

送信ヘッダーは以下のようになります。

POST /oauth1/access/ HTTP/1.1
Authorization: OAuth oauth_consumer_key="fbQ9KPUmdDLP", oauth_nonce="cEsC2kNG16ETXhjmjCb4wOe0otwH6fhN", oauth_signature="KgLDxSAYSbkGh1htQwPPZ8PMoKw%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1451915663", oauth_token="CZpmXOtP98rsvrBk668QZZxE", oauth_version="1.0", oauth_verifier="Etrc3Asa5EYRikFxUN6WMzJT"
Host: wp-api.dev
Connection: close
User-Agent: Paw/2.2.7 (Macintosh; OS X/10.9.5) GCDHTTPRequest
Content-Length: 0

Step1 でリクエストトークンを取得した時と同様に、今回も

  • client key / secret
  • oauth_token , oauth_token_secret
  • nonce, verifier, timestamp

を利用して文字列生成やハッシュ化を経て、 oauth_signature が送信されます。ここでも *_secret という名前のものは送信しないけれども、それらを利用して作られた oauth_signature は送信しています。サーバ側でもkey や token およびそれらとペアになる secret は保持していますので、アクセストークンを渡してよいクライアントなのかの判断が可能になる仕組みです。

確認が済むと、新しい oauth_token と oauth_token_secret が発行されます。今後は、これを古いものと差し替えて利用します。アクセス権を示すトークンをアクセストークンと呼び、アクセストークンをリクエストするためのトークンをリクエストトークンと呼ぶ、というわけですね。

ここまでで OAuth 認証は完了です。

アクセストークンを使ってみる

アクセストークンを得られたので、投稿の更新や認証無しでは取得できないデータが受け取れるようになったかどうかを確認してみます。くわしくは次回以降やりますが、今回はこれらが本当に使えるのかどうかだけ確認してみましょう。

Paw の oauth_token と oauth_token_secret を差し替えて、これまでは取得できなかった投稿のメタデータをリクエストしてみます。

post_id 1 の投稿のメタデータをリクエストしたら json でデータが返ってきた!
post_id 1 の投稿のメタデータをリクエストしたら json でデータが返ってきた!

ちゃんと取れていますね。リクエストを見てみましょう。

GET /wp-json/wp/v2/posts/1/meta HTTP/1.1
Authorization: OAuth oauth_consumer_key="fbQ9KPUmdDLP", oauth_nonce="UqeSQCG8m1C4Px5QMcH2Je5OhEc6OQr7", oauth_signature="ZfwTyXEm3UE6CQJ5E52D82QNU%2B0%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1451916352", oauth_token="PdJ2r3GHkbKlwQNKb5OZoZRE", oauth_version="1.0"
Host: wp-api.dev
Connection: close
User-Agent: Paw/2.2.7 (Macintosh; OS X/10.9.5) GCDHTTPRequest

仕組みとしては同じみたいですね。

というわけで、今日はここまで!

↓ プラグインを作る方々への本、書きました。 ↓

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

WP REST API の OAuth 認証の方法と何が起こっているのかとなぜそんなことをしているのか」への2件のフィードバック