OpenIDのRP側Assertion出来てOP側も作り始めた

RP側のAssertion出来た。
OP側も作り出してみた。
 

RP

取れたのはいいんだけど、OAuthを調べた後だと、どこの馬の骨とも知れないRPに安々とデータを渡していいものか…と思ってしまう。
 
前回調べたYadisは「XRDSファイルの場所を辿る」って部分も含めてYadisプロトコルだったらしい。
class Yadis{}はclass Xrds{}に変更した。
 
OAuthと同じ感覚でclass OpenID_Consumer{}としてたけど、仕様に合わせるべくclass OpenID_RP{}に変更。
 
データの保存は、abstract class OpenID_Store{}ってのを作って、実装はそれを継承させる。
どうせ一時のデータだから、serializeしてMemcacheに入れる事に。
class OpenID_Store_Memcache extends OpenID_Store{}とした。
 

Assertion
<?php
class OpenID_RP
{
	// 〜〜〜 略 〜〜〜

	public function assertion()
	{
		if (OpenID_Utils::getOpenIDParam('mode')=='id_res') {
			if (!is_null(OpenID_Utils::getOpenIDParam('invalidate_handle'))) {
				throw new OpenID_Exception('Invalidate handle');
			}
			if ($this->_store) {
				$assoc_cache = $this->_store->getAssociation(OpenID_Utils::getOpenIDParam('assoc_handle'));
				$auth_cache = $this->_store->getAuthorization(OpenID_Utils::getOpenIDParam('assoc_handle'));

				if (isset($assoc_cache, $auth_cache)) {
					$this->_assoc_req = $assoc_cache[0];
					$this->_assoc_res = $assoc_cache[1];
					$this->setAssocType($this->_assoc_res['assoc_type']);
					$this->setSessionType(@$this->_assoc_res['session_type']);
					OpenID_Utils::debug('assertion cache association req', $this->_assoc_req);
					OpenID_Utils::debug('assertion cache association res', $this->_assoc_res);
					OpenID_Utils::debug('assertion cache authorization', $auth_cache);

					// verify return_to
					if ($auth_cache['openid.return_to']!=OpenID_Utils::getOpenIDParam('return_to')) {
						throw new OpenID_Exception('Assertion error: invalid return_to');
					}

					// verify sig

					$signed = explode(',', OpenID_Utils::getOpenIDParam('signed', ''));
					$plain_arr = array();
					foreach ($signed as $key) {
						$plain_arr[$key] = OpenID_Utils::getOpenIDParam($key);
					}
					$plain = OpenID_Utils::buildKeyValue($plain_arr);
					$sig = hash_hmac($this->getAssocAlgo(), $plain, base64_decode($this->_assoc_res['mac_key']), true);
					if ($sig==base64_decode(OpenID_Utils::getOpenIDParam('sig'))) {
						return true;
					}
					OpenID_Utils::debug('assertion sig', base64_encode($sig));
					throw new OpenID_Exception('Assertion error: invalid openid.sig');
				}
			}
			throw new OpenID_Exception('Assertion error: not found assiciation');
		}
		return false;
	}

	// 〜〜〜 略 〜〜〜
}

 

間接通信 エラー時の間接応答

間接通信でエラーがあった場合、RPにリダイレクトすると仕様に書いてある。
だけど巷のOPは、Assertionで不正なパラメータを送ってもリダイレクトせずに、ユーザエージェントに対してエラーが発生した事を伝えてきた。(mixiYahoo!)
この辺は意外と適当なのかもしれない。
 

5.2.3. エラー時の間接応答
不正な要求があった場合や無効な引数が含まれていた場合、OP は、ユーザエージェントを "openid.return_to" URL 値にリダイレクトしなければならない (MUST)。ただし、値が存在し、その値が有効な URL である場合に限る。

Final: OpenID Authentication 2.0 - 最終版

 

OP

XRDSファイルはContent-Type: application/xrds+xmlmixiのXRDSのパスだけを修正したもの。
index.phpではヘッダにX-XRDS-Location: http://〜〜〜を出力。
OpenID APIは、送られてきたopenid.modeをみて叩くメソッドを分岐させた。
作ったメソッドは、associateとcheckid_setupだけ。
多分Sregとかにも対応させた場合、ここの分岐がちょっと変わる。
こっちもデータはMemcacheにいれた。
で、自作RPライブラリとうまい事やり取り出来たから、OpenID Enabledのサンプルと連携させてみたら、Stateless modeを使ってるらしく駄目だった。
 
OpenID Enabledのライブラリだと、こんなフォームを生成してる。

<form accept-charset="UTF-8" enctype="application/x-www-form-urlencoded" id="openid_message" action="https://mixi.jp/openid_server.pl" method="post">
<input type="hidden" name="openid.ns" value="http://specs.openid.net/auth/2.0" />
<input type="hidden" name="openid.ns.sreg" value="http://openid.net/extensions/sreg/1.1" />
<input type="hidden" name="openid.ns.pape" value="http://specs.openid.net/extensions/pape/1.0" />
<input type="hidden" name="openid.sreg.required" value="nickname" />
<input type="hidden" name="openid.sreg.optional" value="fullname,email" />
<input type="hidden" name="openid.pape.preferred_auth_policies" value="" />
<input type="hidden" name="openid.realm" value="http://localhost:80/php-openid-2.1.3/examples/consumer/" />
<input type="hidden" name="openid.mode" value="checkid_setup" />
<input type="hidden" name="openid.return_to" value="http://localhost:80/php-openid-2.1.3/examples/consumer/finish_auth.php?janrain_nonce=2010-05-24T06%3A38%3A51ZGKt15U" />
<input type="hidden" name="openid.identity" value="http://specs.openid.net/auth/2.0/identifier_select" />
<input type="hidden" name="openid.claimed_id" value="http://specs.openid.net/auth/2.0/identifier_select" />
<input type="hidden" name="openid.assoc_handle" value="1274683131:8tIfki7Unl1kBvOu7h5B:4526f2f65d" />
<input type="submit" value="Continue" />
</form>

 
Stateless modeについて詳しく書いてあるサイトがあんまりなかった…。
ちょっと古い記事だけど、多分ここが詳しい。
 
斜め読みOpenID Authentication 1.1 Modes - Yet Another Hackadelic
http://d.hatena.ne.jp/ZIGOROu/20070322/1174560537