File: Zend_OpenId-Consumer.xml

package info (click to toggle)
zendframework 1.12.9%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: jessie-kfreebsd
  • size: 133,584 kB
  • sloc: xml: 1,311,829; php: 570,173; sh: 170; makefile: 125; sql: 121
file content (827 lines) | stat: -rw-r--r-- 35,014 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
<?xml version="1.0" encoding="UTF-8"?>
<!-- EN-Revision: 24249 -->
<!-- Reviewed: no -->
<sect1 id="zend.openid.consumer">
    <title>Zend_OpenId_Consumer Grundlagen</title>

    <para>
        <classname>Zend_OpenId_Consumer</classname> kann verwendet werden um OpenID
        Authentifizierung auf Webseiten zu implementieren.
    </para>

    <sect2 id="zend.openid.consumer.authentication">
        <title>OpenID Authentifikation</title>

        <para>
            Aus der Sicht eines Website Entwicklers, geschieht die Authentifikation von OpenID in
            drei Schritten:
        </para>

        <orderedlist>
            <listitem>
                <para>Zeige das OpenID Authentifikations Formular</para>
            </listitem>

            <listitem>
                <para>Akzeptiere die OpenID Identität und übergib Sie an den OpenID Provider</para>
            </listitem>

            <listitem>
                <para>Überprüfe die Antwort des OpenID Providers</para>
            </listitem>
        </orderedlist>

        <para>
            Das OpenID Authentifikations Protokoll benötigt aktuell mehrere, aber viele von Ihnen
            sind innerhalb von <classname>Zend_OpenId_Consumer</classname> gekapselt, und deshalb
            für den Entwickler transparent.
        </para>

        <para>
            Der End-Benutzer initiiert den OpenID Authentifikations Prozess indem er Seine oder Ihre
            Identifikations Daten in der entsprechenden Form übermittelt. Das folgende Beispiel
            zeigt ein einfaches Formular das einen OpenID Identifikator akzeptiert. Es gilt zu
            beachten dass das Beispiel nur einen Login demonstriert.
        </para>

        <example id="zend.openid.consumer.example-1">
            <title>Das einfache OpenID Login Formular</title>

            <programlisting language="php"><![CDATA[
<html><body>
<form method="post" action="example-1_2.php"><fieldset>
<legend>OpenID Login</legend>
<input type="text" name="openid_identifier">
<input type="submit" name="openid_action" value="login">
</fieldset></form></body></html>
]]></programlisting>
        </example>

        <para>
            Dieses Formular übergibt bei der Übertragung eine OpenID Identität an das folgende
            <acronym>PHP</acronym> Skript welches den zweiten Schritt der Authentifizierung
            durchführt. Das <acronym>PHP</acronym> Skript muss in diesem Schritt nur die
            <methodname>Zend_OpenId_Consumer::login()</methodname> Methode aufrufen. Das erste
            Argument dieser Methode akzeptiert eine OpenID Identität, und das zweite ist die
            <acronym>URL</acronym> des Skripts das den dritten und letzten Schritt der
            Authentifizierung behandelt.
        </para>

        <example id="zend.openid.consumer.example-1_2">
            <title>Der Authentifizierungs Anfrage Handler</title>

            <programlisting language="php"><![CDATA[
$consumer = new Zend_OpenId_Consumer();
if (!$consumer->login($_POST['openid_identifier'], 'example-1_3.php')) {
    die("OpenID Login fehlgeschlagen.");
}
]]></programlisting>
        </example>

        <para>
            Die <methodname>Zend_OpenId_Consumer::login()</methodname> Methode führt eine Suche nach
            einem gegebenen Identifikator durch und findet, bei Erfolg, die Adresse des Identitäts
            Providers und dessen Lokalen Idenzifizierer durch. Dann erstellt es eine Assoziation zum
            gegebenen Provider sodas beide, die Site und der Provider, um das gleiche Geheimnis
            teilen das verwendet wird um nachfolgende Nachrichten zu verschlüsseln. Letztendlich
            wird eine Authentifikations Anfrage an den Provider übergeben. Diese Anfrage leitet den
            Web-Browser des End-Benutzers zu einer OpenID Server Site um, wo der Benutzer die
            Möglichkeit habt den Authentifizierungs Prozess fortzuführen.
        </para>

        <para>
            Ein OpenID Provider fragt nochmalerweise Benutzer nach Ihrem Passwort (wenn Sie vorher
            noch nicht angemeldet waren), wenn der Benutzer dieser Site vertraut und welche
            Informationen zu der Site zurückgegeben werden können. Diese Interaktionen sind für den
            OpenID Konsument nicht sichtbar sodas es für Ihn keine Möglichkeit gibt das
            Benutzerpasswort oder andere Informationen zu bekommen bei denen der Benutzer nicht
            gesagt hat das der OpenId Provider Sie teilen darf.
        </para>

        <para>
            Bei Erfolg wird <methodname>Zend_OpenId_Consumer::login()</methodname> nicht
            zurückkommen, sondern eine <acronym>HTTP</acronym> Umleitung durchführt. Trotzdem wird
            im Falle eine Fehler ein <constant>FALSE</constant> zurückgeben wird. Fehler können
            durch eine ungültige Identität, einen Provider der nicht antwortet, Kommunikations
            Fehler, usw. auftreten.
        </para>

        <para>
            Der dritte Schritt der Authentifikation wird durch die Antwort vom OpenID Provider
            initiiert, nachdem dieser das Benutzerpasswort authentifiziert hat. Diese Antwort wird
            indirekt, als <acronym>HTTP</acronym> Umleitung übergeben, indem der Webbrowsers des
            End-Benutzers verwendet wird. Der Konsument muß nun einfach prüfen ob die Antwort gültig
            ist.
        </para>

        <example id="zend.openid.consumer.example-1_3">
            <title>Der Authentifizierungs Antwort Prüfer</title>

            <programlisting language="php"><![CDATA[
$consumer = new Zend_OpenId_Consumer();
if ($consumer->verify($_GET, $id)) {
    echo "GÜLTIG ". htmlspecialchars($id);
} else {
    echo "UNGÜLTIG" . htmlspecialchars($id);
}
]]></programlisting>
        </example>

        <para>
            Diese Prüfung wird durchgeführt indem die
            <classname>Zend_OpenId_Consumer::verify</classname> Methode verwendet wird, welche ein
            ganzes Array von HTTP Anfrage Argumenten entgegennimmt und prüft ob diese Antwort durch
            den OpenID Provider richtig signiert wurde. Sie kann die erhaltete OpenID Identität, die
            vom Endbenutzer im ersten Schritt angegeben wurde, zuordnen, indem ein zweites,
            optionales, Argument eingegeben wird.
        </para>
    </sect2>

    <sect2 id="zend.openid.consumer.combine">
        <title>Alle Schritte in einer Seite kombinieren</title>

        <para>
            Das folgende Beispiel kombiniert alle drei Schritte in einem Skript. Es bietet keine
            neuen Funktionalitäten. Der Vorteil der Verwendung eines einzelnen Skripts ist, das
            Entwickler keine <acronym>URL</acronym>'s für das Skript definieren muss, das den
            nächsten Schritt durchführt. Standardmäßig verwenden alle Schritte die gleiche
            <acronym>URL</acronym>. Trotzdem enthält das Skript nun etwas Dispatchcode um den
            korrekten Code für jeden Schritt der Authentifikation aufzurufen.
        </para>

        <example id="zend.openid.consumer.example-2">
            <title>Das komplette Skript für ein OpenID Login</title>

            <programlisting language="php"><![CDATA[
$status = "";
if (isset($_POST['openid_action']) &&
    $_POST['openid_action'] == "login" &&
    !empty($_POST['openid_identifier'])) {

    $consumer = new Zend_OpenId_Consumer();
    if (!$consumer->login($_POST['openid_identifier'])) {
        $status = "OpenID Login fehlgeschlagen.";
    }
} else if (isset($_GET['openid_mode'])) {
    if ($_GET['openid_mode'] == "id_res") {
        $consumer = new Zend_OpenId_Consumer();
        if ($consumer->verify($_GET, $id)) {
            $status = "GÜLTIG " . htmlspecialchars($id);
        } else {
            $status = "UNGÜLTIG " . htmlspecialchars($id);
        }
    } else if ($_GET['openid_mode'] == "cancel") {
        $status = "ABGEBROCHEN";
    }
}
?>
<html><body>
<?php echo "$status<br>" ?>
<form method="post">
<fieldset>
<legend>OpenID Login</legend>
<input type="text" name="openid_identifier" value=""/>
<input type="submit" name="openid_action" value="login"/>
</fieldset>
</form>
</body></html>
]]></programlisting>
        </example>

        <para>
            Zusätzlich unterscheidet dieser Code zwischen abgebrochen und ungültigen
            Authentifizierungs Antworten. Der Provider gibt eine abgebrochene Antwort zurück, wenn
            der Identitäts Provider die gegebene Identität nicht unterstützt, der Benutzer nicht
            angemeldet ist, oder der Benutzer der Seite nicht vertraut. Eine ungültige Antwort zeigt
            an das die Antwort dem OpenId Protokoll nicht entspricht oder nicht korrekt signiert
            wurde.
        </para>
    </sect2>

    <sect2 id="zend.openid.consumer.realm">
        <title>Konsumenten Bereiche</title>

        <para>
            Wenn eine OpenID-aktivierte Site eine Authentifikations Anfrage an einen Provider
            übergibt, identifiziert diese sich selbst mit einer Bereichs <acronym>URL</acronym>.
            Diese <acronym>URL</acronym> kann als Root der vertrauten Site betrachtet werden. Wenn
            der Benutzer der Bereichs <acronym>URL</acronym> vertraut, dann sollte er oder Sie das
            auch bei der passenden und den untergeordneten <acronym>URL</acronym>s tun.
        </para>

        <para>
            Standardmäßig wird die Bereichs <acronym>URL</acronym> automatisch auf die
            <acronym>URL</acronym> des Verzeichnisses gesetzt indem das Login Skript ist. Dieser
            Standardwert ist für die meisten, aber nicht alle, Fälle ausreichend. Manchmal sollte
            einer komplette Domain, und nicht einem Verzeichnis vertraut werden. Oder sogar einer
            Kombination von verschiedenen Servern in einer Domain.
        </para>

        <para>
            Um den Standardwert zu überschreiben müssen Entwickler die Bereichs
            <acronym>URL</acronym> als drittes Argument an die
            <classname>Zend_OpenId_Consumer::login</classname> Methode übergeben. Im folgenden
            Beispiel fragt eine einzelne Interaktion nach vertrauten Zugriff auf alle php.net Sites.
        </para>

        <example id="zend.openid.consumer.example-3_2">
            <title>Authentifizierungs Anfrage für spezielle Bereiche</title>

            <programlisting language="php"><![CDATA[
$consumer = new Zend_OpenId_Consumer();
if (!$consumer->login($_POST['openid_identifier'],
                      'example-3_3.php',
                      'http://*.php.net/')) {
    die("OpenID Login fehlgeschlagen.");
}
]]></programlisting>
        </example>

        <para>
            Dieses Beispiel implementiert nur den zweiten Schritt der Authentifikation; der erste
            und dritte Schritt sind die identisch mit dem ersten Beispiel.
        </para>
    </sect2>

    <sect2 id="zend.openid.consumer.check">
        <title>Sofortige Prüfung</title>

        <para>
            In einigen Fällen muß eine Anwendung nur prüfen ob ein Benutzer bereits auf einem
            vertrauten OpenID Server eingeloggt ist ohne einer Interaktion mit dem Benutzer. Die
            <classname>Zend_OpenId_Consumer::check</classname> Methode führt genau das durch. Sie
            wird mit den gleichen Argumenten wie <classname>Zend_OpenId_Consumer::login</classname>
            ausgeführt, aber Sie zeigt dem Benutzer keine OpenID Serverseiten. Aus Sicht des
            Benutzers ist dieser Prozess transparent, und es scheint als ob er die Site nie
            verlässt. Der dritte Schritt ist erfolgreich wenn der Benutzer bereits angemeldet ist
            und der Site vertraut, andernfalls ist er erfolglos.
        </para>

        <example id="zend.openid.consumer.example-4">
            <title>Sofortige Prüfung ohne Interaktion</title>

            <programlisting language="php"><![CDATA[
$consumer = new Zend_OpenId_Consumer();
if (!$consumer->check($_POST['openid_identifier'], 'example-4_3.php')) {
    die("OpenID Login fehlgeschlaten.");
}
]]></programlisting>
        </example>

        <para>
            Das Beispiel implementiert nur den zweiten Schritt der Authentifikation; der erste und
            dritte Schritt sind dem obigen Beispiel ähnlich.
        </para>
    </sect2>

    <sect2 id="zend.openid.consumer.storage">
        <title>Zend_OpenId_Consumer_Storage</title>

        <para>
            Es gibt drei Schritte beim Authentifizierungs Prozess von OpenID, und jeder wird durch
            eine separate <acronym>HTTP</acronym> Anfrage durchgeführt. Um die Informationen
            zwischen den Anfragen zu speichern verwendet <classname>Zend_OpenId_Consumer</classname>
            einen internen Speicher.
        </para>

        <para>
            Entwickler müssen sich nicht notwendigerweise um die Speicherung kümmern weil
            <classname>Zend_OpenId_Consumer</classname> standardmäßig einen dateibasierten Speicher
            im temporären Verzeichnis verwendet, ähnlich wie <acronym>PHP</acronym> Sessions.
            Trotzdem ist dieser Speicher nicht in allen Situationen richtig. Einige Entwickler
            wollen Informationen in einer Datenbank speichern, während andere einen üblichen Speicher
            für große Server-Farmen verwenden wollen. Glücklicherweise können Entwickler den
            Standardspeicher sehr einfach mit Ihrem eigenen tauschen. Um einen eigenen
            Speichermechanismus zu spezifizieren muß nur die
            <classname>Zend_OpenId_Consumer_Storage</classname> Klasse erweitert werden und diese
            Unterklasse dem <classname>Zend_OpenId_Consumer</classname> Konstruktor im ersten
            Argument übergeben werden.
        </para>

        <para>
            Das folgende Beispiel demonstriert einen einfachen Speicher Mechanismus der
            <classname>Zend_Db</classname> als sein Backend verwendet und drei Gruppen von
            Funktionen bereitstellt. Der erste Gruppe enthält Funktionen für die Arbeit mit
            Assoziationen, während die zweite Gruppe erkannte Informationen cacht, und die dritte
            Gruppe kann verwendet werden um zu prüfen ob die Antwort eindeutig ist. Die Klasse kann
            einfach mit bestehenden oder neuen Datenbanken verwendet werden; wenn die benötigten
            Tabellen nicht existieren, wird er Sie erstellen.
        </para>

        <example id="zend.openid.consumer.example-5">
            <title>Datenbank Speicher</title>

            <programlisting language="php"><![CDATA[
class DbStorage extends Zend_OpenId_Consumer_Storage
{
    private $_db;
    private $_association_table;
    private $_discovery_table;
    private $_nonce_table;

    // Übergib das Zend_Db_Adapter Objekt und die Namen der
    // benötigten Tabellen
    public function __construct($db,
                                $association_table = "association",
                                $discovery_table = "discovery",
                                $nonce_table = "nonce")
    {
        $this->_db = $db;
        $this->_association_table = $association_table;
        $this->_discovery_table = $discovery_table;
        $this->_nonce_table = $nonce_table;
        $tables = $this->_db->listTables();

        // Erstelle die Assoziationstabellen wenn Sie nicht existieren
        if (!in_array($association_table, $tables)) {
            $this->_db->getConnection()->exec(
                "create table $association_table (" .
                " url     varchar(256) not null primary key," .
                " handle  varchar(256) not null," .
                " macFunc char(16) not null," .
                " secret  varchar(256) not null," .
                " expires timestamp" .
                ")");
        }

        // Erstelle die Discoverytabellen wenn Sie nicht existieren
        if (!in_array($discovery_table, $tables)) {
            $this->_db->getConnection()->exec(
                "create table $discovery_table (" .
                " id      varchar(256) not null primary key," .
                " realId  varchar(256) not null," .
                " server  varchar(256) not null," .
                " version float," .
                " expires timestamp" .
                ")");
        }

        // Erstelle die Nouncetabellen wenn Sie nicht existieren
        if (!in_array($nonce_table, $tables)) {
            $this->_db->getConnection()->exec(
                "create table $nonce_table (" .
                " nonce   varchar(256) not null primary key," .
                " created timestamp default current_timestamp" .
                ")");
        }
    }

    public function addAssociation($url,
                                   $handle,
                                   $macFunc,
                                   $secret,
                                   $expires)
    {
        $table = $this->_association_table;
        $secret = base64_encode($secret);
        $this->_db->insert($table, array(
            'url'     => $url,
            'handle'  => $handle,
            'macFunc' => $macFunc,
            'secret'  => $secret,
            'expires' => $expires,
        ));
        return true;
    }

    public function getAssociation($url,
                                   &$handle,
                                   &$macFunc,
                                   &$secret,
                                   &$expires)
    {
        $table = $this->_association_table;
        $this->_db->delete(
            $table, $this->_db->quoteInto('expires < ?', time())
        );
        $select = $this-_db->select()
                ->from($table, array('handle', 'macFunc', 'secret', 'expires'))
                ->where('url = ?', $url);
        $res = $this->_db->fetchRow($select);

        if (is_array($res)) {
            $handle  = $res['handle'];
            $macFunc = $res['macFunc'];
            $secret  = base64_decode($res['secret']);
            $expires = $res['expires'];
            return true;
        }
        return false;
    }

    public function getAssociationByHandle($handle,
                                           &$url,
                                           &$macFunc,
                                           &$secret,
                                           &$expires)
    {
        $table = $this->_association_table;
        $this->_db->delete(
            $table, $this->_db->quoteInto('expires < ', time())
        );
        $select = $this->_db->select()
                ->from($table, array('url', 'macFunc', 'secret', 'expires')
                ->where('handle = ?', $handle);
        $res = $select->fetchRow($select);

        if (is_array($res)) {
            $url     = $res['url'];
            $macFunc = $res['macFunc'];
            $secret  = base64_decode($res['secret']);
            $expires = $res['expires'];
            return true;
        }
        return false;
    }

    public function delAssociation($url)
    {
        $table = $this->_association_table;
        $this->_db->query("delete from $table where url = '$url'");
        return true;
    }

    public function addDiscoveryInfo($id,
                                     $realId,
                                     $server,
                                     $version,
                                     $expires)
    {
        $table = $this->_discovery_table;
        $this->_db->insert($table, array(
            'id'      => $id,
            'realId'  => $realId,
            'server'  => $server,
            'version' => $version,
            'expires' => $expires,
        ));

        return true;
    }

    public function getDiscoveryInfo($id,
                                     &$realId,
                                     &$server,
                                     &$version,
                                     &$expires)
    {
        $table = $this->_discovery_table;
        $this->_db->delete($table, $this->quoteInto('expires < ?', time()));
        $select = $this->_db->select()
                ->from($table, array('realId', 'server', 'version', 'expires'))
                ->where('id = ?', $id);
        $res = $this->_db->fetchRow($select);

        if (is_array($res)) {
            $realId  = $res['realId'];
            $server  = $res['server'];
            $version = $res['version'];
            $expires = $res['expires'];
            return true;
        }
        return false;
    }

    public function delDiscoveryInfo($id)
    {
        $table = $this->_discovery_table;
        $this->_db->delete($table, $this->_db->quoteInto('id = ?', $id));
        return true;
    }

    public function isUniqueNonce($nonce)
    {
        $table = $this->_nonce_table;
        try {
            $ret = $this->_db->insert($table, array(
                'nonce' => $nonce,
            ));
        } catch (Zend_Db_Statement_Exception $e) {
            return false;
        }
        return true;
    }

    public function purgeNonces($date=null)
    {
    }
}

$db = Zend_Db::factory('Pdo_Sqlite',
    array('dbname'=>'/tmp/openid_consumer.db'));
$storage = new DbStorage($db);
$consumer = new Zend_OpenId_Consumer($storage);
]]></programlisting>
        </example>

        <para>
            Dieses Beispiel zeigt keinen OpenID Authentifikations Code, aber dieser Code würde der
            gleiche sein wie der für die anderen Beispiel in diesem Kapitel.
        </para>
    </sect2>

    <sect2 id="zend.openid.consumer.sreg">
        <title>Einfache Registrations Erweiterung</title>

        <para>
            Zusätzlich zur Authentifikation kann OpenID Standard für einen leichtgewichtigen
            Profiltausch verwendet werden, um Informationen über einen Benutzer über mehrere Sites
            hinweg portabel zu machen. Dieses Feature wird nicht durch die OpenID Authentifikations
            Spezifikation abgedeckt, aber vom OpenID Einfachen Registrierungs Erweiterungs Protokoll
            unterstützt. Dieses Protokoll erlaubt es OpenID-aktivierten Sites nach Informationen
            über End-Benutzern von OpenID Providers zu fragen. Diese Informationen können folgendes
            beinhalten:
        </para>

        <itemizedlist>
            <listitem>
                <para>
                    <emphasis>nickname</emphasis> - ein UTF-8 String den der End-Benutzer als
                    Spitzname verwendet.
                </para>
            </listitem>

            <listitem>
                <para>
                    <emphasis>email</emphasis> - die Email Adresse des Benutzers wie in Sektion
                    3.4.1 von RFC2822 spezifiziert.
                </para>
            </listitem>

            <listitem>
                <para>
                    <emphasis>fullname</emphasis> - eine UTF-8 String Repräsentation des kompletten
                    Namens des Benutzers.
                </para>
            </listitem>

            <listitem>
                <para>
                    <emphasis>dob</emphasis> - das Geburtsdatum des Benutzers im Format
                    'YYYY-MM-DD'. Jeder Wert dessen Repräsentation weniger als die speifizierte
                    Anzahl an Ziffern in diesem Format verwendet sollte mit Nullen aufgefüllt
                    werden. In anderen Worten, die Länge dieses Wertes muß immer 10 sein. Wenn der
                    Benutzer irgendeinen Teil dieses Wertes (z.B. Jahr, Monat oder Tag) nicht
                    angeben will, dann muß dieser auf Null gesetzt werden. Wenn ein Benutzer zum
                    Beispiel angeben will das sein Geburtsdatum in das Jahr 1980 fällt, aber nicht
                    den Monat oder Tag angeben will, dann sollte der zurückgegebene Wert
                    '1980-00-00' sein.
                </para>
            </listitem>

            <listitem>
                <para>
                    <emphasis>gender</emphasis> - das Geschlecht des Benutzers: "M" für männlich,
                    "F" für weiblich
                </para>
            </listitem>

            <listitem>
                <para>
                    <emphasis>postcode</emphasis> - ein UTF-8 String der dem Postleitzahl System des
                    Landes des End-Benutzers entspricht
                </para>
            </listitem>

            <listitem>
                <para>
                    <emphasis>country</emphasis> - das Land des Wohnsitzes des Benutzers wie
                    in ISO3166 spezifiziert
                </para>
            </listitem>

            <listitem>
                <para>
                    <emphasis>language</emphasis> - die bevorzugte Sprache des Benutzers wie in
                    ISO639 spezifiziert
                </para>
            </listitem>

            <listitem>
                <para>
                    <emphasis>timezone</emphasis> - ein <acronym>ASCII</acronym> String von der
                    Zeitzonen Datenbank. Zum Beispiel, "Europe/Paris" oder "America/Los_Angeles".
                </para>
            </listitem>
        </itemizedlist>

        <para>
            Eine OpenID-aktivierte Web-Seite kann nach jeder beliebigen Kombination dieser Felder
            fragen. Sie kann auch einige Informationen strikt fordern und es Benutzern erlauben
            zusätzliche Informationen anzubieten oder zu verstecken. Das folgende Beispiel
            Instanziiert die <classname>Zend_OpenId_Extension_Sreg</classname> Klasse die einen
            <emphasis>nickname</emphasis> (Spitzname) benötigt und optional eine
            <emphasis>email</emphasis> (E-Mail) und einen <emphasis>fullname</emphasis>
            (vollständigen Namen) benötigt.
        </para>

        <example id="zend.openid.consumer.example-6_2">
            <title>Anfragen mit einer einfachen Registrations Erweiterung senden</title>

            <programlisting language="php"><![CDATA[
$sreg = new Zend_OpenId_Extension_Sreg(array(
    'nickname'=>true,
    'email'=>false,
    'fullname'=>false), null, 1.1);
$consumer = new Zend_OpenId_Consumer();
if (!$consumer->login($_POST['openid_identifier'],
                      'example-6_3.php',
                      null,
                      $sreg)) {
    die("OpenID Login fehlgeschlagen.");
}
]]></programlisting>
        </example>

        <para>
            Wie man sieht akzeptiert der <classname>Zend_OpenId_Extension_Sreg</classname>
            Konstruktor ein Array von OpenId Feldern. Das Array hat den Namen der Felder als Indezes
            zu einem Flag das anzeigt ob das Feld benötigt wird oder nicht.
            <constant>TRUE</constant> bedeutet der Wert wird benötigt und <constant>FALSE</constant>
            bedeutet das Feld ist optional. Die Methode
            <classname>Zend_OpenId_Consumer::login</classname> akzeptiert eine Erweiterung oder ein
            Array von Erweiterungen als sein viertes Argument.
        </para>

        <para>
            Im dritten Schritt der Authentifikation sollte das
            <classname>Zend_OpenId_Extension_Sreg</classname> Objekt an
            <classname>Zend_OpenId_Consumer::verify</classname> übergeben werden. Anschließend wird
            die Methode <classname>Zend_OpenId_Extension_Sreg::getProperties</classname>, bei
            erfolgreicher Authentifizierung, ein assoziatives Array von benötigten Feldern
            zurückgeben.
        </para>

        <example id="zend.openid.consumer.example-6_3">
            <title>Antworten mit einer einfachen Registierungs Erweiterung prüfen</title>

            <programlisting language="php"><![CDATA[
$sreg = new Zend_OpenId_Extension_Sreg(array(
    'nickname'=>true,
    'email'=>false,
    'fullname'=>false), null, 1.1);
$consumer = new Zend_OpenId_Consumer();
if ($consumer->verify($_GET, $id, $sreg)) {
    echo "GÜLTIG " . htmlspecialchars($id) . "<br>\n";
    $data = $sreg->getProperties();
    if (isset($data['nickname'])) {
        echo "Spitzname: " . htmlspecialchars($data['nickname']) . "<br>\n";
    }
    if (isset($data['email'])) {
        echo "Email: " . htmlspecialchars($data['email']) . "<br>\n";
    }
    if (isset($data['fullname'])) {
        echo "Vollständiger Name: " . htmlspecialchars($data['fullname'])
           . "<br>\n";
    }
} else {
    echo "UNGÜLTIG " . htmlspecialchars($id);
}
]]></programlisting>
        </example>

        <para>
            Wenn das <classname>Zend_OpenId_Extension_Sreg</classname> Objekt ohne Argumente
            erstellt wurde, sollte der Benutzercode selbst das Vorhandensein der benötigten Daten
            prüfen. Trotzdem, wenn das Objekt mit der gleichen Liste an benötigten Feldern wie im
            zweiten Schritt erstellt wird, wird es automatisch die Existenz der benötigten Daten
            prüfen. In diesem Fall wird <classname>Zend_OpenId_Consumer::verify</classname>
            <constant>FALSE</constant> zurückgeben wenn irgendeines der benötigten Felder fehlt.
        </para>

        <para>
            <classname>Zend_OpenId_Extension_Sreg</classname> verwendet standardmäßig die Version
            1.0 weil die Spezifikation der Version 1.1 noch nicht fertiggestellt wurde. Trotzdem
            unterstützen einige Bibliotheken die Version 1.0 nicht vollständig. Zum Beispiel
            benötigt www.myopenid.com einen SREG Namensraum in den Anfragen der nur in 1.1 vorhanden
            ist. Um mit so einem Server zu Arbeiten muß man die Version 1.1 explizit im
            <classname>Zend_OpenId_Extension_Sreg</classname> Konstruktor setzen.
        </para>

        <para>
            Das zweite Argument des <classname>Zend_OpenId_Extension_Sreg</classname> Konstruktors
            ist eine Policy <acronym>URL</acronym>, die dem Benutzer durch den Identitäts Provider
            zur Verfügung gestellt werden sollte.
        </para>
    </sect2>

    <sect2 id="zend.openid.consumer.auth">
        <title>Integration mit Zend_Auth</title>

        <para>
            Zend Framework bietet eine spezielle Klasse für die Unterstützung von Benutzer
            Authentifikation: <classname>Zend_Auth</classname>. Diese Klasse kann zusammen mit
            <classname>Zend_OpenId_Consumer</classname> verwendet werden. Das folgende Beispiel
            zeigt wie <classname>OpenIdAdapter</classname> das
            <classname>Zend_Auth_Adapter_Interface</classname> mit der
            <methodname>authenticate()</methodname> Methode implementiert. Diese führt eine
            Authentifikations Anfrage und Verifikation durch.
        </para>

        <para>
            Der große Unterschied zwischen diesem Adapter und dem bestehenden ist, das er mit zwei
            <acronym>HTTP</acronym> Anfragen arbeitet und einen Dispatch code enthält um den zweiten
            oder dritten Schritt der OpenID Authentifikation durchzuführen.
        </para>

        <example id="zend.openid.consumer.example-7">
            <title>Zend_Auth Adapter für OpenID</title>

            <programlisting language="php"><![CDATA[
class OpenIdAdapter implements Zend_Auth_Adapter_Interface {
    private $_id = null;

    public function __construct($id = null) {
        $this->_id = $id;
    }

    public function authenticate() {
        $id = $this->_id;
        if (!empty($id)) {
            $consumer = new Zend_OpenId_Consumer();
            if (!$consumer->login($id)) {
                $ret = false;
                $msg = "Authentifizierung fehlgeschlagen.";
            }
        } else {
            $consumer = new Zend_OpenId_Consumer();
            if ($consumer->verify($_GET, $id)) {
                $ret = true;
                $msg = "Authentifizierung erfolgreich";
            } else {
                $ret = false;
                $msg = "Authentifizierung fehlgeschlagen";
            }
        }
        return new Zend_Auth_Result($ret, $id, array($msg));
    }
}

$status = "";
$auth = Zend_Auth::getInstance();
if ((isset($_POST['openid_action']) &&
     $_POST['openid_action'] == "login" &&
     !empty($_POST['openid_identifier'])) ||
    isset($_GET['openid_mode'])) {
    $adapter = new OpenIdAdapter(@$_POST['openid_identifier']);
    $result = $auth->authenticate($adapter);
    if ($result->isValid()) {
        Zend_OpenId::redirect(Zend_OpenId::selfURL());
    } else {
        $auth->clearIdentity();
        foreach ($result->getMessages() as $message) {
            $status .= "$message<br>\n";
        }
    }
} else if ($auth->hasIdentity()) {
    if (isset($_POST['openid_action']) &&
        $_POST['openid_action'] == "logout") {
        $auth->clearIdentity();
    } else {
        $status = "Du bist angemeldet als " . $auth->getIdentity() . "<br>\n";
    }
}
?>
<html><body>
<?php echo htmlspecialchars($status);?>
<form method="post"><fieldset>
<legend>OpenID Login</legend>
<input type="text" name="openid_identifier" value="">
<input type="submit" name="openid_action" value="login">
<input type="submit" name="openid_action" value="logout">
</fieldset></form></body></html>
]]></programlisting>
        </example>

        <para>
            Mit <classname>Zend_Auth</classname> wird die Identität des End-Benutzes in den Session
            Daten gespeichert. Sie kann mit <classname>Zend_Auth::hasIdentity</classname> und
            <classname>Zend_Auth::getIdentity</classname> geprüft werden.
        </para>
    </sect2>

    <sect2 id="zend.openid.consumer.mvc">
        <title>Integration mit Zend_Controller</title>

        <para>
            Zuletzt ein paar Worte über die Integration in Model-View-Controller Anwendungen: Solche
            Zend Framework Anwendungen werden implementiert durch Verwenden der
            <classname>Zend_Controller</classname> Klasse und Sie verwenden die
            <classname>Zend_Controller_Response_Http</classname> Klasse um <acronym>HTTP</acronym>
            Antworten vorzubereiten und an den Web Browser des Benutzers zurückzusenden.
        </para>

        <para>
            <classname>Zend_OpenId_Consumer</classname> bietet keine GUI Möglichkeiten aber es führt
            <acronym>HTTP</acronym> Umleitungen bei erflgreichen
            <classname>Zend_OpenId_Consumer::login</classname> und
            <classname>Zend_OpenId_Consumer::check</classname> durch. Diese Umleitungen könnten
            nicht richtig funktionieren, oder sogar überhaupt nicht, wenn einige Daten bereits an
            den Web Browser gesendet wurden. Um <acronym>HTTP</acronym> Umleitungen im
            <acronym>MVC</acronym> Code richtig durchzuführen sollte die echte
            <classname>Zend_Controller_Response_Http</classname> als letztes Argument an
            <classname>Zend_OpenId_Consumer::login</classname> oder
            <classname>Zend_OpenId_Consumer::check</classname> gesendet werden.
        </para>
    </sect2>
</sect1>