| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392 |
- <?xml version="1.0" encoding="UTF-8"?>
- <!-- Reviewed: no -->
- <sect1 id="zend.infocard.basics">
- <title>Introduction</title>
- <para>
- The <classname>Zend_InfoCard</classname> component implements relying-party
- support for Information Cards. Information Cards are used for identity
- management on the internet and authentication of users to web sites. The web sites
- that the user ultimately authenticates to are called <emphasis>relying-parties</emphasis>.
- </para>
- <para>
- Detailed information about information cards and their importance to the
- internet identity metasystem can be found on the <ulink
- url="http://www.identityblog.com/">IdentityBlog</ulink>.
- </para>
- <sect2 id="zend.infocard.basics.theory">
- <title>Basic Theory of Usage</title>
- <para>
- Usage of <classname>Zend_InfoCard</classname> can be done one of two ways:
- either as part of the larger <classname>Zend_Auth</classname> component via
- the <classname>Zend_InfoCard</classname> authentication adapter or as a
- stand-alone component. In both cases an information card can be
- requested from a user by using the following <acronym>HTML</acronym> block in your
- <acronym>HTML</acronym> login form:
- </para>
- <programlisting language="html"><![CDATA[
- <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>
- ]]></programlisting>
- <para>
- In the example above, the <property>requiredClaims</property>
- <param> tag is used to identify pieces of information known as claims (i.e.
- person's first name, last name) which the web site (a.k.a "relying party") needs in
- order a user to authenticate using an information card. For your reference, the full
- <acronym>URI</acronym> (for instance the <emphasis>givenname</emphasis> claim) is as
- follows:
- <filename>http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname</filename>
- </para>
- <para>
- When the above <acronym>HTML</acronym> is activated by a user (clicks on it), the
- browser will bring up a card selection program which not only shows
- them which information cards meet the requirements of the site, but
- also allows them to select which information card to use if multiple
- meet the criteria. This information card is transmitted as an <acronym>XML</acronym>
- document to the specified POST <acronym>URL</acronym> and is ready to be
- processed by the <classname>Zend_InfoCard</classname> component.
- </para>
- <para>
- Note, Information cards can only be <acronym>HTTP</acronym> <acronym>POST</acronym>ed to
- <acronym>SSL</acronym>-encrypted <acronym>URL</acronym>s. Please consult your web
- server's documentation on how to set up <acronym>SSL</acronym> encryption.
- </para>
- </sect2>
- <sect2 id="zend.infocard.basics.auth">
- <title>Using as part of Zend_Auth</title>
- <para>
- In order to use the component as part of the <classname>Zend_Auth</classname>
- authentication system, you must use the provided
- <classname>Zend_Auth_Adapter_InfoCard</classname> to do so (not available in
- the standalone <classname>Zend_InfoCard</classname> distribution). An example
- of its usage is shown below:
- </para>
- <programlisting language="php"><![CDATA[
- <?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>
- ]]></programlisting>
- <para>
- In the example above, we first create an instance of the
- <classname>Zend_Auth_Adapter_InfoCard</classname> and pass the <acronym>XML</acronym>
- data posted by the card selector into it. Once an instance has been created you
- must then provide at least one <acronym>SSL</acronym> certificate public/private key
- pair used by the web server that received the <acronym>HTTP</acronym>
- <acronym>POST</acronym>. These files are used to validate the destination of the
- information posted to the server and are a requirement when using Information Cards.
- </para>
- <para>
- Once the adapter has been configured, you can then use the standard
- <classname>Zend_Auth</classname> facilities to validate the provided
- information card token and authenticate the user by examining the
- identity provided by the <methodname>getIdentity()</methodname> method.
- </para>
- </sect2>
- <sect2 id="zend.infocard.basics.standalone">
- <title>Using the Zend_InfoCard component standalone</title>
- <para>
- It is also possible to use the <classname>Zend_InfoCard</classname> component as a
- standalone component by interacting with the
- <classname>Zend_InfoCard</classname> class directly. Using the
- <classname>Zend_InfoCard</classname> class is very similar to its use with the
- <classname>Zend_Auth</classname> component. An example of its use is shown below:
- </para>
- <programlisting language="php"><![CDATA[
- <?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>
- ]]></programlisting>
- <para>
- In the example above, we use the <classname>Zend_InfoCard</classname> component
- independently to validate the token provided by the user. As was the
- case with the <classname>Zend_Auth_Adapter_InfoCard</classname>, we create an
- instance of <classname>Zend_InfoCard</classname> and then set one or more
- <acronym>SSL</acronym> certificate public/private key pairs used by the web server. Once
- configured, we can use the <methodname>process()</methodname> method to process
- the information card and return the results.
- </para>
- </sect2>
- <sect2 id="zend.infocard.basics.claims">
- <title>Working with a Claims object</title>
- <para>
- Regardless of whether the <classname>Zend_InfoCard</classname> component is used as
- a standalone component or as part of <classname>Zend_Auth</classname> via
- <classname>Zend_Auth_Adapter_InfoCard</classname>, the ultimate
- result of the processing of an information card is a
- <classname>Zend_InfoCard_Claims</classname> object. This object contains the
- assertions (a.k.a. claims) made by the submitting user based on the
- data requested by your web site when the user authenticated. As
- shown in the examples above, the validity of the information card
- can be ascertained by calling the
- <methodname>Zend_InfoCard_Claims::isValid()</methodname> method. Claims
- themselves can either be retrieved by simply accessing the
- identifier desired (i.e. <property>givenname</property>) as a property of
- the object or through the <methodname>getClaim()</methodname> method.
- </para>
- <para>
- In most cases you will never need to use the <methodname>getClaim()</methodname>
- method. However, if your <property>requiredClaims</property> mandate that
- you request claims from multiple different sources/namespaces then
- you will need to extract them explicitly using this method (simply
- pass it the full <acronym>URI</acronym> of the claim to retrieve its value from within
- the information card). Generally speaking however, the
- <classname>Zend_InfoCard</classname> component will set the default
- <acronym>URI</acronym> for claims to be the one used the most frequently within the
- information card itself and the simplified property-access method can be used.
- </para>
- <para>
- As part of the validation process, it is the developer's responsibility to
- examine the issuing source of the claims contained within the
- information card and to decide if that source is a trusted source of
- information. To do so, the <methodname>getIssuer()</methodname> method is
- provided within the <classname>Zend_InfoCard_Claims</classname> object which
- returns the <acronym>URI</acronym> of the issuer of the information card claims.
- </para>
- </sect2>
- <sect2 id="zend.infocard.basics.attaching">
- <title>Attaching Information Cards to existing accounts</title>
- <para>
- It is possible to add support for information cards to an existing
- authentication system by storing the private personal identifier
- (PPI) to a previously traditionally-authenticated account and
- including at least the
- <filename>http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier</filename>
- claim as part of the <property>requiredClaims</property> of the request. If
- this claim is requested then the <classname>Zend_InfoCard_Claims</classname>
- object will provide a unique identifier for the specific card that
- was submitted by calling the <methodname>getCardID()</methodname> method.
- </para>
- <para>
- An example of how to attach an information card to an existing
- traditional-authentication account is shown below:
- </para>
- <programlisting language="php"><![CDATA[
- // ...
- 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");
- }
- }
- ]]></programlisting>
- </sect2>
- <sect2 id="zend.infocard.basics.adapters">
- <title>Creating Zend_InfoCard Adapters</title>
- <para>
- The <classname>Zend_InfoCard</classname> component was designed to allow for
- growth in the information card standard through the use of a modular
- architecture. At this time, many of these hooks are unused and can be
- ignored, but there is one class that should be written for
- any serious information card implementation: the
- <classname>Zend_InfoCard</classname> adapter.
- </para>
- <para>
- The <classname>Zend_InfoCard</classname> adapter is used as a callback
- mechanism within the component to perform various tasks, such as
- storing and retrieving Assertion IDs for information cards when they
- are processed by the component. While storing the assertion IDs of
- submitted information cards is not necessary, failing to do so opens
- up the possibility of the authentication scheme being compromised
- through a replay attack.
- </para>
- <para>
- To prevent this, one must implement the
- <classname>Zend_InfoCard_Adapter_Interface</classname> and set an
- instance of this interface prior to calling either the
- <methodname>process()</methodname> (standalone) or
- <methodname>authenticate()</methodname> method as a <classname>Zend_Auth</classname>
- adapter. To set this interface, the <methodname>setAdapter()</methodname> method should
- be used. In the example below, we set a <classname>Zend_InfoCard</classname> adapter and
- use it in our application:
- </para>
- <programlisting language="php"><![CDATA[
- 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']);
- ]]></programlisting>
- </sect2>
- </sect1>
- <!--
- vim:se ts=4 sw=4 et:
- -->
|