1 <?xml version="1.0" encoding="UTF-8"?>
3 <sect1 id="zend.infocard.basics">
4 <title>Introduction</title>
7 The <classname>Zend_InfoCard</classname> component implements relying-party
8 support for Information Cards. Information Cards are used for identity
9 management on the internet and authentication of users to web sites. The web sites
10 that the user ultimately authenticates to are called <emphasis>relying-parties</emphasis>.
14 Detailed information about information cards and their importance to the
15 internet identity metasystem can be found on the <ulink
16 url="http://www.identityblog.com/">IdentityBlog</ulink>.
19 <sect2 id="zend.infocard.basics.theory">
20 <title>Basic Theory of Usage</title>
23 Usage of <classname>Zend_InfoCard</classname> can be done one of two ways:
24 either as part of the larger <classname>Zend_Auth</classname> component via
25 the <classname>Zend_InfoCard</classname> authentication adapter or as a
26 stand-alone component. In both cases an information card can be
27 requested from a user by using the following <acronym>HTML</acronym> block in your
28 <acronym>HTML</acronym> login form:
31 <programlisting language="html"><![CDATA[
32 <form action="http://example.com/server" method="POST">
33 <input type='image' src='/images/ic.png' align='center'
34 width='120px' style='cursor:pointer' />
35 <object type="application/x-informationCard"
37 <param name="tokenType"
38 value="urn:oasis:names:tc:SAML:1.0:assertion" />
39 <param name="requiredClaims"
40 value="http://.../claims/privatepersonalidentifier
41 http://.../claims/givenname
42 http://.../claims/surname" />
48 In the example above, the <code>requiredClaims</code>
49 <code><param></code> tag is used to identify pieces of
50 information known as claims (i.e. person's first name, last name)
51 which the web site (a.k.a "relying party") needs in order a user to
52 authenticate using an information card. For your reference, the full
53 <acronym>URI</acronym> (for instance the <code>givenname</code> claim) is as follows:
54 <code>http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname</code>
58 When the above <acronym>HTML</acronym> is activated by a user (clicks on it), the
59 browser will bring up a card selection program which not only shows
60 them which information cards meet the requirements of the site, but
61 also allows them to select which information card to use if multiple
62 meet the criteria. This information card is transmitted as an <acronym>XML</acronym>
63 document to the specified POST <acronym>URL</acronym> and is ready to be
64 processed by the <classname>Zend_InfoCard</classname> component.
68 Note, Information cards can only be <code>HTTP POST</code>ed to
69 <acronym>SSL</acronym>-encrypted <acronym>URL</acronym>s. Please consult your web
70 server's documentation on how to set up <acronym>SSL</acronym> encryption.
74 <sect2 id="zend.infocard.basics.auth">
75 <title>Using as part of Zend_Auth</title>
78 In order to use the component as part of the <classname>Zend_Auth</classname>
79 authentication system, you must use the provided
80 <classname>Zend_Auth_Adapter_InfoCard</classname> to do so (not available in
81 the standalone <classname>Zend_InfoCard</classname> distribution). An example
82 of its usage is shown below:
85 <programlisting language="php"><![CDATA[
87 if (isset($_POST['xmlToken'])) {
89 $adapter = new Zend_Auth_Adapter_InfoCard($_POST['xmlToken']);
91 $adapter->addCertificatePair('/usr/local/Zend/apache2/conf/server.key',
92 '/usr/local/Zend/apache2/conf/server.crt');
94 $auth = Zend_Auth::getInstance();
96 $result = $auth->authenticate($adapter);
98 switch ($result->getCode()) {
99 case Zend_Auth_Result::SUCCESS:
100 $claims = $result->getIdentity();
101 print "Given Name: {$claims->givenname}<br />";
102 print "Surname: {$claims->surname}<br />";
103 print "Email Address: {$claims->emailaddress}<br />";
104 print "PPI: {$claims->getCardID()}<br />";
106 case Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID:
107 print "The Credential you provided did not pass validation";
110 case Zend_Auth_Result::FAILURE:
111 print "There was an error processing your credentials.";
115 if (count($result->getMessages()) > 0) {
117 var_dump($result->getMessages());
124 <div id="login" style="font-family: arial; font-size: 2em;">
125 <p>Simple Login Demo</p>
127 <input type="submit" value="Login" />
128 <object type="application/x-informationCard" name="xmlToken">
129 <param name="tokenType"
130 value="urn:oasis:names:tc:SAML:1.0:assertion" />
131 <param name="requiredClaims"
132 value="http://.../claims/givenname
133 http://.../claims/surname
134 http://.../claims/emailaddress
135 http://.../claims/privatepersonalidentifier" />
142 In the example above, we first create an instance of the
143 <classname>Zend_Auth_Adapter_InfoCard</classname> and pass the <acronym>XML</acronym>
144 data posted by the card selector into it. Once an instance has been created you
145 must then provide at least one <acronym>SSL</acronym> certificate public/private key
146 pair used by the web server that received the <code>HTTP
147 POST</code>. These files are used to validate the destination
148 of the information posted to the server and are a requirement when
149 using Information Cards.
153 Once the adapter has been configured, you can then use the standard
154 <classname>Zend_Auth</classname> facilities to validate the provided
155 information card token and authenticate the user by examining the
156 identity provided by the <methodname>getIdentity()</methodname> method.
160 <sect2 id="zend.infocard.basics.standalone">
161 <title>Using the Zend_InfoCard component standalone</title>
164 It is also possible to use the <classname>Zend_InfoCard</classname> component as a
165 standalone component by interacting with the
166 <classname>Zend_InfoCard</classname> class directly. Using the
167 <classname>Zend_InfoCard</classname> class is very similar to its use with the
168 <classname>Zend_Auth</classname> component. An example of its use is shown below:
171 <programlisting language="php"><![CDATA[
173 if (isset($_POST['xmlToken'])) {
174 $infocard = new Zend_InfoCard();
175 $infocard->addCertificatePair('/usr/local/Zend/apache2/conf/server.key',
176 '/usr/local/Zend/apache2/conf/server.crt');
178 $claims = $infocard->process($_POST['xmlToken']);
180 if($claims->isValid()) {
181 print "Given Name: {$claims->givenname}<br />";
182 print "Surname: {$claims->surname}<br />";
183 print "Email Address: {$claims->emailaddress}<br />";
184 print "PPI: {$claims->getCardID()}<br />";
186 print "Error Validating identity: {$claims->getErrorMsg()}";
191 <div id="login" style="font-family: arial; font-size: 2em;">
192 <p>Simple Login Demo</p>
194 <input type="submit" value="Login" />
195 <object type="application/x-informationCard" name="xmlToken">
196 <param name="tokenType"
197 value="urn:oasis:names:tc:SAML:1.0:assertion" />
198 <param name="requiredClaims"
199 value="http://.../claims/givenname
200 http://.../claims/surname
201 http://.../claims/emailaddress
202 http://.../claims/privatepersonalidentifier" />
209 In the example above, we use the <classname>Zend_InfoCard</classname> component
210 independently to validate the token provided by the user. As was the
211 case with the <classname>Zend_Auth_Adapter_InfoCard</classname>, we create an
212 instance of <classname>Zend_InfoCard</classname> and then set one or more
213 <acronym>SSL</acronym> certificate public/private key pairs used by the web server. Once
214 configured, we can use the <methodname>process()</methodname> method to process
215 the information card and return the results.
219 <sect2 id="zend.infocard.basics.claims">
220 <title>Working with a Claims object</title>
223 Regardless of whether the <classname>Zend_InfoCard</classname> component is used as
224 a standalone component or as part of <classname>Zend_Auth</classname> via
225 <classname>Zend_Auth_Adapter_InfoCard</classname>, the ultimate
226 result of the processing of an information card is a
227 <classname>Zend_InfoCard_Claims</classname> object. This object contains the
228 assertions (a.k.a. claims) made by the submitting user based on the
229 data requested by your web site when the user authenticated. As
230 shown in the examples above, the validity of the information card
231 can be ascertained by calling the
232 <methodname>Zend_InfoCard_Claims::isValid()</methodname> method. Claims
233 themselves can either be retrieved by simply accessing the
234 identifier desired (i.e. <code>givenname</code>) as a property of
235 the object or through the <methodname>getClaim()</methodname> method.
239 In most cases you will never need to use the <methodname>getClaim()</methodname>
240 method. However, if your <code>requiredClaims</code> mandate that
241 you request claims from multiple different sources/namespaces then
242 you will need to extract them explicitly using this method (simply
243 pass it the full <acronym>URI</acronym> of the claim to retrieve its value from within
244 the information card). Generally speaking however, the
245 <classname>Zend_InfoCard</classname> component will set the default
246 <acronym>URI</acronym> for claims to be the one used the most frequently within the
247 information card itself and the simplified property-access method can be used.
251 As part of the validation process, it is the developer's responsibility to
252 examine the issuing source of the claims contained within the
253 information card and to decide if that source is a trusted source of
254 information. To do so, the <methodname>getIssuer()</methodname> method is
255 provided within the <classname>Zend_InfoCard_Claims</classname> object which
256 returns the <acronym>URI</acronym> of the issuer of the information card claims.
260 <sect2 id="zend.infocard.basics.attaching">
261 <title>Attaching Information Cards to existing accounts</title>
264 It is possible to add support for information cards to an existing
265 authentication system by storing the private personal identifier
266 (PPI) to a previously traditionally-authenticated account and
267 including at least the
268 <code>http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier</code>
269 claim as part of the <code>requiredClaims</code> of the request. If
270 this claim is requested then the <classname>Zend_InfoCard_Claims</classname>
271 object will provide a unique identifier for the specific card that
272 was submitted by calling the <methodname>getCardID()</methodname> method.
276 An example of how to attach an information card to an existing
277 traditional-authentication account is shown below:
280 <programlisting language="php"><![CDATA[
282 public function submitinfocardAction()
284 if (!isset($_REQUEST['xmlToken'])) {
285 throw new ZBlog_Exception('Expected an encrypted token ' .
286 'but was not provided');
289 $infoCard = new Zend_InfoCard();
290 $infoCard->addCertificatePair(SSL_CERTIFICATE_PRIVATE,
291 SSL_CERTIFICATE_PUB);
294 $claims = $infoCard->process($request['xmlToken']);
295 } catch(Zend_InfoCard_Exception $e) {
296 // TODO Error processing your request
300 if ($claims->isValid()) {
301 $db = ZBlog_Data::getAdapter();
303 $ppi = $db->quote($claims->getCardID());
304 $fullname = $db->quote("{$claims->givenname} {$claims->surname}");
306 $query = "UPDATE blogusers
308 real_name = $fullname
309 WHERE username='administrator'";
313 } catch(Exception $e) {
314 // TODO Failed to store in DB
317 $this->view->render();
321 ZBlog_Exception("Infomation card failed security checks");
327 <sect2 id="zend.infocard.basics.adapters">
328 <title>Creating Zend_InfoCard Adapters</title>
331 The <classname>Zend_InfoCard</classname> component was designed to allow for
332 growth in the information card standard through the use of a modular
333 architecture. At this time, many of these hooks are unused and can be
334 ignored, but there is one class that should be written for
335 any serious information card implementation: the
336 <classname>Zend_InfoCard</classname> adapter.
340 The <classname>Zend_InfoCard</classname> adapter is used as a callback
341 mechanism within the component to perform various tasks, such as
342 storing and retrieving Assertion IDs for information cards when they
343 are processed by the component. While storing the assertion IDs of
344 submitted information cards is not necessary, failing to do so opens
345 up the possibility of the authentication scheme being compromised
346 through a replay attack.
350 To prevent this, one must implement the
351 <classname>Zend_InfoCard_Adapter_Interface</classname> and set an
352 instance of this interface prior to calling either the
353 <methodname>process()</methodname> (standalone) or
354 <methodname>authenticate()</methodname> method as a <classname>Zend_Auth</classname>
355 adapter. To set this interface, the <methodname>setAdapter()</methodname> method should
356 be used. In the example below, we set a <classname>Zend_InfoCard</classname> adapter and
357 use it in our application:
360 <programlisting language="php"><![CDATA[
361 class myAdapter implements Zend_InfoCard_Adapter_Interface
363 public function storeAssertion($assertionURI,
367 /* Store the assertion and its conditions by ID and URI */
370 public function retrieveAssertion($assertionURI, $assertionID)
372 /* Retrieve the assertion by URI and ID */
375 public function removeAssertion($assertionURI, $assertionID)
377 /* Delete a given assertion by URI/ID */
381 $adapter = new myAdapter();
383 $infoCard = new Zend_InfoCard();
384 $infoCard->addCertificatePair(SSL_PRIVATE, SSL_PUB);
385 $infoCard->setAdapter($adapter);
387 $claims = $infoCard->process($_POST['xmlToken']);