PRESSMAN*Tech

HTML5, CSS3からWordPress, AWS, Elasticsearchまで。プレスマンのスタッフが綴る技術ブログ。

OAuthをPHPでイチから書いてみた(その1)

いろいろなWebサービスで採用されているOAuthは、OAuth用のライブラリを使ってしまうと簡単に実装できます。
しかし、OAuthがなんなのか、いったい何をしているのかイマイチわからないまま実装されてしまうこともある?ような気がしました。
そこで、どのようにしてWebサービスへのアクセスが可能になるかをイチからプログラムして理解しようという目論みです。OAuth2.0がでて手続きが簡単になるとの話も聞きますが1.0です。

何が例によってだかわかりませんが、とりあえず例によってTwitterでやってみます。

下記の様な画面を出すのを「その1」の目標にしましょう。
Twitter OAuth

まずは、下記URLにアクセスしてアプリケーションの登録をしてみます。


https://dev.twitter.com/apps

登録すると、

Consumer key : pD4dm6IQHa6jhtge82Fg
Consumer secret : BfTtokF9bfb5Fim6BsPaEg9Fo3y4FM6kPlCQQiRPKA4
Request token URL : https://api.twitter.com/oauth/request_token
Access token URL : https://api.twitter.com/oauth/access_token
Authorize URL : https://api.twitter.com/oauth/authorize

のような感じでキーとURLがもらえます。(上記のキーはテキトーな文字列を入力しただけです。)
これを使って認証していきます。

まず、OAuthの目的を明確にしてみましょう。
OAuthには3つの登場人物(サービス)がいます。

  1. ユーザー
  2. サービスプロバイダー(例:twitter)
  3. コンシューマー(サービスプロバイダーのデータにアクセスしたいサービス、アプリなど)

そして、こういう関係になっています。

ユーザーはコンシューマーのサービスを使いたい

コンシューマーのサービスはサービスプロバイダーのデータを使う必要がある

でもユーザーはサービスプロバイダーに登録してある自分のID,パスワードはコンシューマーに教えたくない

さてどうしよう?

ここでOAuthの出番です。細かい話は抜きにして単純化してしまえば、この問題を解決するためにアクセストークンというキーをサービスプロバイダーが発行する。そして、それを使えばコンシューマーがデータにアクセスできるようになるというわけです。しかし、、このアクセストークンを取得するまでの道のりが長いんです。

アクセストークンを取得するにはまずリクエストトークンというキーをサービスプロバイダーに発行してもらう必要があります。今回の目標はユーザーに認証を求める画面を出すまでですが、他の言い方をすれば、リクエストトークン発行の第一段階といった所です。

PHPのソースは以下のようになります。OAuthの仕組みを理解する目的なので関数やクラスは作らず一直線になっています。

<?php
//titterでアプリ登録するともらえる情報
$oauthConfig = array(
'callbackUrl' => 'http://net.pressmantech.com/twitter/accesskey.php',
'authorizeUrl' => 'https://api.twitter.com/oauth/authorize',
'requestTokenUrl' => 'https://api.twitter.com/oauth/request_token',
'accessTokenUrl' => 'https://api.twitter.com/oauth/access_token',
'consumerKey' => 'pD4dm6IQHa6jhtge82Fg',
'consumerSecret' => 'BfTtokF9bfb5Fim6BsPaEg9Fo3y4FM6kPlCQQiRPKA4'
);
//今回はGETでアクセスしてリクエストトークンを取得する。
$method = 'GET';
//ランダムな文字列。
$nonce =  md5(uniqid(rand(), true));
//Unixタイムスタンプ
$timestamp = time();
//リクエストトークンを取得するのに必要な情報をまとめる。
$authorization = array(
'oauth_nonce' => $nonce,
'oauth_signature_method' => 'HMAC-SHA1', //TwitterではHMAC-SHA1に固定
'oauth_timestamp' => $timestamp,
'oauth_consumer_key' => $oauthConfig['consumerKey'],
'oauth_version' => '1.0'
);
/**
* コールバックURL
* コールバックURLは「アプリを認証」ボタンを押したときにリダイレクトされるURL
* リダイレクトされるときにリクエストトークンと検証用のキーがパラメーター
* としてついてきます。
* コールバックURLが無いときはoobという文字列が入ってout of band modeで
* 動作します。これはリダイレクトされずにPINコードが表示されてそれを使って
* 次のステップへ進むモードです。
*/
if($oauthConfig['callbackUrl']){
$authorization['oauth_callback'] = $oauthConfig['callbackUrl'];
}else{
$authorization['oauth_callback'] = 'oob';
}
/**
* キーのアルファベット順でソートします。
* $authorization配列の中身を使ってリクエストトークンを発行してもらうための
* 署名(oauth_signature)を作ります。
* その際にアルファベット順で並べて文字列にしたものを署名用のメッセージとして
* 使う決まりがあるためソートします。
*/
ksort($authorization);
/**
* signature base string の生成(oauth_signatureを作るときのメッセージ部分)
* rawurlencodeはPHP5.3とそれより前では仕様が違う(5.2以前では~を%7Eに変換)
* ので注意。ここでは5.3でのコードです。
* やっていることは簡単で
* 配列の中身のキーと値をURLエンコードしたものをイコールで&で結合する。
* それをさらにURLエンコードする。
* HTTPメソッドとリクエストトークンURLをURLエンコードして&で結合
* 下記のURLが非常に分かり易い。
* http://developer.yahoo.co.jp/other/oauth/signinrequest.html
*
* こんな感じになります。(適当に改行をいれてあります。)
* GET&https%3A%2F%2Fapi.twitter.com%2Foauth%2Frequest_token&
* oauth_callback%3Dhttp%253A%252F%252Fnet.pressmantech.com%252F
* %26oauth_consumer_key%3DpD4dm6IQHa6jhtge82Fg
* %26oauth_nonce%3D174bd408ddbc0e3bf3603898f0f3d97b
* %26oauth_signature_method%3DHMAC-SHA1
* %26oauth_timestamp%3D1307515593
* %26oauth_version%3D1.0
*/
$signatureBaseString = '';
foreach($authorization as $key => $val){
$signatureBaseString .= $key . '=' . rawurlencode($val) . '&';
}
$signatureBaseString = substr($signatureBaseString,0,-1);
$signatureBaseString = $method . '&' .
rawurlencode($oauthConfig['requestTokenUrl']) . '&' .
rawurlencode($signatureBaseString);
/**
* signing keyの生成(oauth_signatureを作るときのキー部分)
* oauth_token_secretがあれば"&"の後ろにつけるのだが今はまだ無い。
* よって&で終わる変なカタチ
*/
$signingKey = rawurlencode($oauthConfig['consumerSecret']) . '&';
/**
* oauth_signatureの生成base64_encodeとhash_hmacの3番目の引数を
* trueにするのを忘れないように。
*/
$authorization['oauth_signature'] =
base64_encode(hash_hmac('sha1',$signatureBaseString,$signingKey,true));
//リクエストトークンをリクエストするURLを生成
$requestUrl = $oauthConfig['requestTokenUrl'] . '?';
foreach($authorization as $key => $val){
$requestUrl .= $key . '=' . rawurlencode($val) .'&';
}
$requestUrl = substr($requestUrl,0,-1);
print $requestUrl;

これを実行すると下記の様なURLを取得できます。(これはアクセスしてもconsumer keyがテキトーなので当然エラーになります。)

正しいキーで生成したリクエストURLだったとしてアクセスしてみると、

のような形で文字列が出力されます。このなかのoauth_token未承認のリクエストトークンです。

このoauth_tokenをAuthorize URLにくっつけて以下のURLを作ります。

そうすると例の認証用ページ(このページの上の方にある画像)が表示されるはずです。「アプリを認証」ボタンを押せばコールバックURLに遷移するし、コールバックURLが空の場合は(つまりoobとしてリクエストトークンを発行した場合)下記の様な画面が出てくるはずです。
PINコード画面

ということで、無事今回の目的にたどりつけました。ただ、これは未承認のリクエストトークンを取得できただけです。実際にはPOSTメソッドを使ってcURLなどで実装するのかなぁと想像してみたりします。

この後は、リクエストトークンを承認させて、アクセストークンを取得といった感じになるわけで以下次号。

投稿者:taichi

taichi の紹介

何でもやりますが、一応プログラマです。
このエントリーをはてなブックマークに追加

私たちと一緒に働きませんか?

株式会社プレスマンでは、プログラミングが大好きな方、仕事を通してさらにスキルを磨きたい方を募集しています。まずは募集職種をご覧の上、お気軽にお問い合わせください。あなたとお会いできるのを楽しみにしています。

プレスマンの採用情報を見る →

コメントは受け付けていません。

▲ 先頭へ戻る