# 導入

`Zend_InfoCard` コンポーネントは、 情報カード (Information Cards) の relying-party サポートを実装したものです。 情報カードは、インターネット上でのユーザ識別情報の管理や ウェブサイトのユーザ認証に用いるものです。 最終的にユーザ認証を行う先のウェブサイトのことを relying-party といいます。

情報カードについて、 あるいはインターネット上の識別メタシステムにおける情報カードの重要性については、 IdentityBlog を参照ください。

## 基本的な使用法

`Zend_InfoCard` の使用法は、 `Zend_Auth` コンポーネントの一部として `Zend_InfoCard` 認証アダプタを使用するか、 あるいは単体のコンポーネントとして使用するかのいずれかです。 どちらの場合についても、ユーザから情報カードを受け取るには HTML のログインフォームの中で次のような HTML ブロックを使用します。

    <form action="http://example.com/server" method="POST">
      <input type='image' src='/images/ic.png' align='center'
            width='120px' style='cursor:pointer' />
      <object type="application/x-informationCard"
              name="xmlToken">
       <param name="tokenType"
             value="urn:oasis:names:tc:SAML:1.0:assertion" />
       <param name="requiredClaims"
             value="http://.../claims/privatepersonalidentifier
             http://.../claims/givenname
             http://.../claims/surname" />
     </object>
    </form>

この例において、`requiredClaims` `<param>` タグで表しているのが、 claim (人の姓名など) と呼ばれる識別情報です。 これは、ウェブサイト ("relying party") が情報カードによる認証を行うために必要となります。

上の HTML をユーザが実行する (クリックする) と、 ブラウザはカード選択プログラムを実行します。 これは、そのサイトの要求を満たす情報カードを表示させるだけでなく、 条件を満たす情報カードが複数存在する場合に好きなものを選択させることができます。 この情報カードは、指定した URL に XML ドキュメントとして `POST` され、これを `Zend_InfoCard` コンポーネントで処理することになります。

情報カードは、SSL で暗号化した URL への `HTTP POST` しかできないことに注意しましょう。 SSL による暗号化を設定する方法については、 ウェブサーバのドキュメントを参照ください。

## Zend_Auth の部品としての使用法

このコンポーネントを `Zend_Auth` 認証システムと組み合わせて使用するには、 `Zend_Auth_Adapter_InfoCard` を使用する必要があります (これは、単体で配布されている `Zend_InfoCard` には存在しません)。 この手法での使用例を以下に示します。

    <?php
    if (isset($_POST['xmlToken'])) {

        $adapter = new Zend_Auth_Adapter_InfoCard($_POST['xmlToken']);

        $adapter->addCertificatePair('/usr/local/Zend/apache2/conf/server.key',
                                     '/usr/local/Zend/apache2/conf/server.crt');

        $auth = Zend_Auth::getInstance();

        $result = $auth->authenticate($adapter);

        switch ($result->getCode()) {
            case Zend_Auth_Result::SUCCESS:
                $claims = $result->getIdentity();
                print "Given Name: {$claims->givenname}<br />";
                print "Surname: {$claims->surname}<br />";
                print "Email Address: {$claims->emailaddress}<br />";
                print "PPI: {$claims->getCardID()}<br />";
                break;
            case Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID:
                print "The Credential you provided did not pass validation";
                break;
            default:
            case Zend_Auth_Result::FAILURE:
                print "There was an error processing your credentials.";
                break;
        }

        if (count($result->getMessages()) > 0) {
            print "<pre>";
            var_dump($result->getMessages());
            print "</pre>";
        }

    }
    ?>
    <hr />
    <div id="login" style="font-family: arial; font-size: 2em;">
    <p>Simple Login Demo</p>
     <form method="post">
      <input type="submit" value="Login" />
       <object type="application/x-informationCard" name="xmlToken">
        <param name="tokenType"
              value="urn:oasis:names:tc:SAML:1.0:assertion" />
        <param name="requiredClaims"
              value="http://.../claims/givenname
                     http://.../claims/surname
                     http://.../claims/emailaddress
                     http://.../claims/privatepersonalidentifier" />
      </object>
     </form>
    </div>

上の例では、まず最初に `Zend_Auth_Adapter_InfoCard` のインスタンスを作成して、 カードセレクタから送信された XML データをそこに渡しています。 インスタンスを作成したら、次に SSL 証明書の秘密鍵/公開鍵 ペアを指定する必要があります。 このペアは、`HTTP POST` を受け取ったウェブサーバで使用しているものです。 これらのファイルを使用して、サーバに送信された情報のあて先の検証を行います。 情報カードを使用するときにはこれらが必要となります。

アダプタの設定がすんだら、あとは `Zend_Auth` の標準機能を使って情報カードトークンの検証を行い、 `getIdentity()` で取得した識別情報をもとにユーザの認証を行います。

## Zend_InfoCard コンポーネント単体での使用法

`Zend_InfoCard` コンポーネントを、 それ単体で使用することも可能です。その場合は `Zend_InfoCard` クラスを直接操作します。 `Zend_InfoCard` クラスの使用法は、`Zend_Auth` コンポーネントと組み合わせて使用する場合とほぼ同じです。 以下に使用例を示します。

    <?php
    if (isset($_POST['xmlToken'])) {
        $infocard = new Zend_InfoCard();
        $infocard->addCertificatePair('/usr/local/Zend/apache2/conf/server.key',
                                      '/usr/local/Zend/apache2/conf/server.crt');

        $claims = $infocard->process($_POST['xmlToken']);

        if($claims->isValid()) {
            print "Given Name: {$claims->givenname}<br />";
            print "Surname: {$claims->surname}<br />";
            print "Email Address: {$claims->emailaddress}<br />";
            print "PPI: {$claims->getCardID()}<br />";
        } else {
            print "Error Validating identity: {$claims->getErrorMsg()}";
        }
    }
    ?>
    <hr />
    <div id="login" style="font-family: arial; font-size: 2em;">
    <p>Simple Login Demo</p>
     <form method="post">
      <input type="submit" value="Login" />
       <object type="application/x-informationCard" name="xmlToken">
        <param name="tokenType"
              value="urn:oasis:names:tc:SAML:1.0:assertion" />
        <param name="requiredClaims"
              value="http://.../claims/givenname
                     http://.../claims/surname
                     http://.../claims/emailaddress
                     http://.../claims/privatepersonalidentifier" />
       </object>
     </form>
    </div>

上の例では、`Zend_InfoCard` コンポーネントを単体で使用して、ユーザが送信したトークンを検証しています。 `Zend_Auth_Adapter_InfoCard` の場合と同様、 `Zend_InfoCard` のインスタンスを作成してから ウェブサーバの SSL 証明書の公開キー/秘密キーペアを設定します。 設定がすんだら、`process()` メソッドで情報カードの処理を行ってその結果を返します。

## Claims オブジェクトの使用法

`Zend_InfoCard` の使用方法 (単体で使用するか、あるいは `Zend_Auth` の一部として `Zend_Auth_Adapter_InfoCard` 経由で使用するか) にかかわらず、情報カードを処理した結果は `Zend_InfoCard_Claims` オブジェクトとして返されます。 このオブジェクトには assertions (claims) が含まれます。 これは、ユーザ認証の際にあなたのサイトが出した要求にもとづいて、 ユーザが送信したデータから作成したものです。 上の例で示したように、情報カードの妥当性を確認するには `Zend_InfoCard_Claims::isValid()` メソッドをコールします。claims そのものを取得するには、 単純に識別子 (`givenname` など) をオブジェクトのプロパティとして指定してアクセスするか、 あるいは `getClaim()` メソッドを使用します。

ほとんどの場合においては `getClaim()` メソッドを使用する必要はありません。 しかし、もし `requiredClaims` が複数の異なるソース/名前空間からの情報を要求している場合は、 それをこのメソッドで明示的に取り出す必要があります (claim の完全な URI を私、情報カードの中からその値を取得します)。 一般論として、`Zend_InfoCard` コンポーネントがデフォルトで設定する claim 用 URI は情報カードの中で最もよく用いられるものです。 この場合は単純にプロパティを使用してアクセスすることができます。

検証処理の中で開発者が行わなければならない部分は、 情報カード内の claim の発行元を調べて それが信頼できる情報元かどうかを判定するところです。 これを行うために、`Zend_InfoCard_Claims` オブジェクトには `getIssuer()` メソッドが用意されています。 このメソッドは、情報カードの claim の発行元 URI を返します。

## 既存のアカウントへの情報カードの添付

既存の認証システムに情報カードのサポートを追加することもできます。 そのためには、private personal identifier (PPI) を昔ながらの認証アカウントに埋め込み、 最低限の claim である `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier` をリクエストの `requiredClaims` に指定します。この claim が要求されると、 `Zend_InfoCard_Claims` オブジェクトはそのカード用の一意な識別子を用意します。 これは、`getCardID()` メソッドによって行います。

情報カードを既存の昔ながらの認証アカウントに添付する例を、 以下に示します。

    // ...
    public function submitinfocardAction()
    {
        if (!isset($_REQUEST['xmlToken'])) {
            throw new ZBlog_Exception('Expected an encrypted token ' .
                                      'but was not provided');
        }

        $infoCard = new Zend_InfoCard();
        $infoCard->addCertificatePair(SSL_CERTIFICATE_PRIVATE,
                                      SSL_CERTIFICATE_PUB);

        try {
            $claims = $infoCard->process($request['xmlToken']);
        } catch(Zend_InfoCard_Exception $e) {
            // TODO Error processing your request
            throw $e;
        }

        if ($claims->isValid()) {
            $db = ZBlog_Data::getAdapter();

            $ppi = $db->quote($claims->getCardID());
            $fullname = $db->quote("{$claims->givenname} {$claims->surname}");

            $query = "UPDATE blogusers
                         SET ppi = $ppi,
                             real_name = $fullname
                       WHERE username='administrator'";

            try {
                $db->query($query);
            } catch(Exception $e) {
                // TODO Failed to store in DB
            }

            $this->view->render();
            return;
        } else {
            throw new
                ZBlog_Exception("Infomation card failed security checks");
        }
    }

## Zend_InfoCard アダプタの作成

`Zend_InfoCard` コンポーネントは、 情報カードの標準規格の変化に対応するために モジュラー構造を採用しています。 現時点では、拡張ポイントの多くは未使用ですので無視できますが、 情報カードの実装においてひとつだけ実装すべき点があります。 それが `Zend_InfoCard_Adapter` です。

`Zend_InfoCard` アダプタは、 コンポーネント内でコールバックを使用してさまざまな処理を行います。 たとえば、コンポーネントが情報カードを処理する際の Assertion ID の保存や取得などを行います。 受け取った情報カードの assertion ID の保存は必須ではありませんが、 もしそれに失敗すると、リプレイ攻撃によって認証が信頼できないものになる可能性が発生します。

これを避けるためには、 `Zend_InfoCard_Adapter_Interface` を実装してそのインスタンスを設定してから `process()` メソッド (単体) あるいは `authenticate()` メソッド (`Zend_Auth` アダプタ) をコールしなければなりません。 このインターフェイスを設定するためのメソッドが `setAdapter()` です。 以下の例では、`Zend_InfoCard` アダプタを設定してアプリケーション内で使用しています。

    class myAdapter implements Zend_InfoCard_Adapter_Interface
    {
        public function storeAssertion($assertionURI,
                                       $assertionID,
                                       $conditions)
        {
            /* Store the assertion and its conditions by ID and URI */
        }

        public function retrieveAssertion($assertionURI, $assertionID)
        {
            /* Retrieve the assertion by URI and ID */
        }

        public function removeAssertion($assertionURI, $assertionID)
        {
            /* Delete a given assertion by URI/ID */
        }
    }

    $adapter  = new myAdapter();

    $infoCard = new Zend_InfoCard();
    $infoCard->addCertificatePair(SSL_PRIVATE, SSL_PUB);
    $infoCard->setAdapter($adapter);

    $claims = $infoCard->process($_POST['xmlToken']);