前々回、OpenIDが流行ってないと書いたが、OpenIDに賛同する気があるなら積極的に実装すべきかと思い、symfonyから簡単にコンシューマー部分を実装してみた。

symfonyにはsfOpenIDPluginという、OpenIDをかなり簡単に実装できるプラグインがあるが、Smart Modeに対応していないこと、OpenID 2.0に対応していないこともあり、別のPHPライブラリである、PHP OpenID Libraryを利用してみる。

なお、詳しい実装の流れは@ITのOpenID特集サイトが詳しい。

まずは、 PHP OpenID Libraryから2.x.x系のライブラリをダウンロード、解凍した中にある「Auth」ディレクトリをsymfonyプロジェクトの「lib」ディレクトリに配置しておこう。

こんな感じ

[project]/apps
[project]/web
.....
[project]/lib/Auth

詳しい実装の話は抜きにして、

/openid/login  ⇒ /openid/tryauth ⇒ OpenID供給サイト(Idp)  ⇒ /openid/finishauth

こんな感じで、太字になっている部分をサイト側は実装すればよい。

まずはmoduleのさくせい。
$ symfony init-module project openid

まずはloginフォーム
loginSuccess.php

PHP:
  1. <?php echo form_tag('/openid/tryauth') ?>
  2. <?php echo form_error('openid_identifier') ?>
  3. <div>OpenID</div>
  4. <div><?php echo input_tag('openid_identifier', '',array('size' => 40)) ?></div>
  5. <p></p>
  6. <div align="left"><?php echo submit_tag('ログイン') ?></div>
  7. </form>
  8. <p></p>
  9. <?php echo link_to('Yahoo IDでログイン','/openid/tryauth?openid_identifier=yahoo.co.jp')?>

実装コードは下記のようになった。
action.class.php

PHP:
  1. class openidActions extends sfActions
  2. {
  3.   /*
  4.    * ログインフォームを表示する処理
  5.    */
  6.   public function executeLogin()
  7.   {
  8.     return sfView::SUCCESS;
  9.   }
  10.   /*
  11.    * ログインフォームからPostされたときのバリデート処理
  12.    */
  13.   public function validateTryauth()
  14.   {
  15.     // OpenIDライブラリがNoticeを出してしまうため止めてます。
  16.     $er = error_reporting();
  17.     if ($er> E_STRICT) {
  18.       error_reporting($er - E_STRICT);
  19.     }
  20.  
  21.     // openid_identifierがユーザの入力したURLが入っている。
  22.     $openid = $this->getRequestParameter('openid_identifier');
  23.     if (!$openid)
  24.     {
  25.       // エラー処理は手抜き。。
  26.       $this->getRequest()->setError('openid_identifier', 'error');
  27.       return false;
  28.     }
  29.     // openidの認証ファイルを格納するディレクトリ(DBにもできる)
  30.     $store_path = '/tmp/_php_tmp';
  31.     if (!file_exists($store_path) && !mkdir($store_path))
  32.     {
  33.       $this->getRequest()->setError('openid_identifier', 'error');
  34.       return false;
  35.     }
  36.  
  37.     $store = new Auth_OpenID_FileStore($store_path);
  38.     $consumer = new Auth_OpenID_Consumer($store);
  39.     $auth_request = $consumer->begin($openid);
  40.     if (!$auth_request)
  41.     {
  42.       $this->getRequest()->setError('openid_identifier', 'error');
  43.       return false;
  44.     }
  45.     //認証時に属性が欲しい場合は指定する。
  46.     $sreg_request = Auth_OpenID_SRegRequest::build(
  47.       array('nickname'),
  48.       array('fullname', 'email'));
  49.     if ($sreg_request)
  50.     {
  51.       $auth_request->addExtension($sreg_request);
  52.     }
  53.  
  54.     //OpenID1.0
  55.     if ($auth_request->shouldSendRedirect())
  56.     {
  57.       // trust_rootは認証されるサイトのURL、return_toは認証後にこちらにリダイレクトするURL
  58.       $redirect_url = $auth_request->redirectURL(
  59.         'http://sample.net/',
  60.         'http://sample.net/openid/finishauth');
  61.       if (Auth_OpenID::isFailure($redirect_url))
  62.       {
  63.         $this->getRequest()->setError('openid_identifier', 'error');
  64.         return false;
  65.       }
  66.       else
  67.       {
  68.         $this->redirect($redirect_url);
  69.       }
  70.     }
  71.     //OpenID2.0
  72.     else
  73.     {
  74.       $form_id = 'openid_message';
  75.       // trust_rootは認証されるサイトのURL、return_toは認証後にこちらにリダイレクトするURL
  76.       $form_html = $auth_request->formMarkup(
  77.         'http://sample.net/',
  78.         'http://sample.net/openid/finishauth',
  79.         false, array('id' => $form_id));
  80.       if (Auth_OpenID::isFailure($form_html))
  81.       {
  82.         $this->getRequest()->setError('openid_identifier', 'error');
  83.         return false;
  84.       }
  85.       else
  86.       {
  87.         $this->getRequest()->setAttribute('openid_html', $form_html);
  88.         $this->getRequest()->setAttribute('form_id', $form_id);
  89.         return true;
  90.       }
  91.     }
  92.   }
  93.   /*
  94.    * ログインフォームからPostされたときの処理
  95.    */
  96.   public function executeTryauth()
  97.   {
  98.   }
  99.   public function handleErrorTryauth()
  100.   {
  101.     $this->forward('openid', 'login');
  102.   }
  103.   /*
  104.    * Idpからリダイレクトされてからの処理
  105.    */
  106.   public function executeFinishauth()
  107.   {
  108.     $er = error_reporting();
  109.     if ($er> E_STRICT) {
  110.       error_reporting($er - E_STRICT);
  111.     }
  112.     $store_path = '/tmp/_php_tmp';
  113.     if (!file_exists($store_path) && !mkdir($store_path))
  114.     {
  115.       $this->getRequest()->setError('openid_identifier', 'error');
  116.       $this->forward('openid', 'index');
  117.     }
  118.     $store = new Auth_OpenID_FileStore($store_path);
  119.     $consumer = new Auth_OpenID_Consumer($store);
  120.  
  121.     $response = $consumer->complete('http://sample.net/openid/finishauth');
  122.     if ($response->status == 'cancel')
  123.     {
  124.       $this->getRequest()->setError('openid_identifier', 'error');
  125.       $this->forward('openid', 'index');
  126.     }
  127.     else if ($response->status == 'failure')
  128.     {
  129.       $this->getRequest()->setError('openid_identifier', 'error');
  130.       $this->forward('openid', 'index');
  131.     }
  132.     else if ($response->status == 'success')
  133.     {
  134.       //認証されたopenid
  135.       $openid = $response->getDisplayIdentifier();
  136.       //ここに認証処理を書けばよい例えば下記のような感じで。
  137.       $this->getUser()->setAuthenticated(true);
  138.       $this->getUser()->addCredential('editor');
  139.       $this->getUser()->setAttribute('openid', $openid, 'user');
  140.  
  141.       //どこかにリダイレクトでも
  142.       $this->redirect('/top/index');
  143.     }
  144.   }
  145. }

処理の流れがわかるように、アクションクラスにすべてつめこんでます。

そして、POST送信用に下記のファイルを用意する。
tryauthSuccess.php

PHP:
  1. <html>
  2. <body onload='document.getElementById("<?php echo $sf_request->getAttribute('form_id') ?>").submit()'>
  3. <?php echo $sf_request->getAttribute('openid_html') ?>
  4. </body>
  5. </html>

これで「yahoo.co.jp」と入力もしくは、リンクをおくだけで認証処理が完了する。

OpenIDの詳細な仕様は知らなくても、流れさえ知ってればある程度は実装できる。

とはいえ、認証部分はサイトの脆弱性に繋がるため、サイトに実装する際はちゃんと理解する必要はあるでしょう。

関連する記事

blogranking←ぽちっとな

<<
>>