Webdesign

OpenID demystified – Teil 1

21. September 2009
von

OpenID ist eine Technologie die bereits die kritische Masse an Unterstützung durch große Player wie Google, Yahoo und Facebook erhalten hat. Als Webentwickler darf man sich die Frage stellen, ob und wie man OpenID für die eigenen Zwecke verwenden kann. Es gibt bereits PHP-Klassen, mit deren Hilfe der Zugang zu OpenID relativ leicht ist. Zwar ist OpenID  auf ersten Blick ein leicht zu verstehendes Konzept, dahinter verstecken sich jedoch zahlreiche Prozesse. Wir wollen die Vorgänge bei einer OpenID-Authentifizierung Schritt für Schritt mit einem PHP-Skript nachvollziehen.

Die Struktur einer OpenID-Authentifizierungsanfrage ist bisweilen sehr komplex, da es für die verschiedenen Schritte oftmals mehrere Protokolle oder Schemata gibt, nach denen Anfragen gesendet werden können. Das folgende Bild soll vereinfacht die Struktur einer Anfrage Schritt für Schritt darstellen.

Flussdiagramm weiter unten

Insgesamt gibt es neun Schritte für eine erfolgreiche Authentifizierung. Im ersten Schritt wird von der Applikation als zum Beispiel ihres Webdienstes, was ein Blog oder ähnlich sein kann, eine Anfrage an den Nutzer gesendet. Das hat die Form eines Login-Feldes – so können Sie Ihren Nutzer bitten, sich einzuloggen, um Kommentare in Ihrem Blog abzusetzen. Im zweiten Schritt wählt der Nutzer OpenID als Login und gibt in das Login-Feld seine OpenID ein.

Daraufhin wird der Discovery-Prozess eingeleitet, bei dem zunächst die OpenID überprüft wird. Hier schaltet sich dann auch zum ersten Mal ihr OpenID Provider ein, indem er der Applikation mitteilt, wo der OpenID-Server liegt. Daraufhin fordert die Applikation die Autorisierung durch einen Login des Nutzers an. Jetzt erscheint für den Nutzer das Login-Formular des OpenID-Providers. Der Nutzer gibt seine Login-Daten ein und willigt dem Zugriff ihrer Applikation auf die Daten des Nutzers ein. Im vorletzten Schritt wird die Profilinformation nach erfolgreichem Login an die Applikation weitergeleitet, diese wiederum ermöglicht dem Nutzer im letzten Schritt Zugriff auf die gesicherten Inhalte. In unserem Beispiel darf unser Nutzer jetzt einen Kommentar in dem Blog absetzen.

Für die technische Umsetzung einer Authentifizierung über OpenID mit PHP gibt es einige Bibliotheken:

  • PHPOpenID von JanRain: Diese Bibliothek gibt es seit 2006, seit 2007 in den Versionen 1 und 2. Das OpenID-Protokoll gibt es in den Versionen 1.1 und 2.0 – leider ist die Kompatibilität nicht sichergestellt, da manche Provider aufgrund von Sicherheitsbedenken voll auf OpenID 2.0 ohne Abwärtskompatibilität setzen. Daher lautet die Empfehlung, mit OpenID 2.0 und damit auch mit den Releases aus der PHPOpenID-Version 2 zu arbeiten. Für Applikationen, die noch nicht auf die Releases für Version 2 umgestiegen sind, wird aber die Version 1-Reihe weiter bedient. Mit der Bibliothek kann man sowohl die Authentifizierung durchführen lassen, als auch selber ein Anbieter von OpenIDs werden. Eine Anwendung der Bibliothek stellt der OpenID-Anbieter myopenid.com dar. Der OpenID-Server dort läuft mit der PHPOpenID-Bibliothek.
  • PEAR Package Proposal von Bill Shupp: Es gab bereits mehrere Proposals für ein PEAR Package. Ein neuer Entwurf wurde erst vor wenigen Wochen von Bill Shupp eingereicht. Der bisherige Entwurf sieht aber bisher nur einen OpenID-Konsumenten vor, also eine Bibliothek zur Authentifizierung. Am Bereitstellen eines OpenIDs-Server, welches ebenfalls noch in die Bibliothek integriert werden soll, wird noch gearbeitet. Bill Shupp bietet ein paar Beispiele basierend auf der Bibliothek, mit denen der Authentifizierungsprozess schrittweise im Debug Modus durchlaufen werden kann. Um die Beispiele auszuprobieren, benötigt man eine OpenID, die man etwa von MyOpenID oder Google, Yahoo und Co. erhalten kann. Eine Liste der großen OpenID-Anbieter finden Sie in der Wikipedia.

Das Flussdiagramm stellt die einzelnen Schritte einer OpenID Authentifizierung schematisch dar.

Im Beispiel soll ein Authentifizierungsprozess auf Basis der PHPOpenID-Bibliothek durchgespielt werden. Das ist an und für sich nichts Großes, aber zeigt die Schritte, die für eine Authentifizierung durchgegangen werden müssen. In einem weiteren Schritt soll das Skript erweitert werden, indem es über die OpenID-Erweiterung AX, was für Attribute Exchange steht, zusätzliche Userdaten auslesen kann. Im allereinfachsten Fall werden keine Nutzerinformationen ausgetauscht – es findet lediglich eine Authentifizierung statt. Man kann jedoch relativ unproblematisch auf folgende Daten zugreifen:

  • Nickname
  • Email
  • Voller Name
  • Geburtsdatum im Format YYYY-MM-DD
  • Geschlecht
  • Postleitzahl
  • Land mit ständigem Wohnsitz, die Ländercodes sind nach ISO3166.
  • Sprache, wobei die Sprachcodes nach ISO639
  • Zeitzone

Dieser erweiterte (aber doch noch sehr beschränkte) Informationsaustausch wurde in der Sreg (Simple Registration Extension) für OpenID definiert. Die Attribute Exchange-Erweiterung zu dem OpenID-Protokoll ermöglicht den erweiterten Austausch von Nutzerdaten, hier finden Sie ein Typenschema. Sie beinhaltet auch Prozeduren, mit denen Fallbacks definiert werden können, sollte ein OpenID-Anbieter gewisse Informationen nicht bereitstellen (vielleicht, weil der OpenID Besitzer dies nicht will).

Authentifizierung mit PHPOpenID

Zunächst müssen Sie sich PHPOpenID herunterladen. Im Endeffekt muss lediglich der Ordner “Auth” in die jeweilige Verzeichnisstruktur eingefügt werden, sodass die Includes auf das richtige Verzeichnis verweisen. Standardmäßig werden die Daten in Flatfiles, also einfachen Textdateien gespeichert. Sie können aber auch MySQL oder PostgreSQL nutzen. Die entsprechenden Bibliotheken werden mitgeliefert.

Alles was Sie für eine Authentifizierung mit einer OpenID benötigen, ist eine OpenID – diese hat in der Regel die Form eines URL. Wenn Sie sich bei MyOpenID.com eine OpenID besorgen, dann hat diese das Format http://ihrname.myopenid.com. Das Login-Formular kann dann wie folgt aussehen:

Das OpenID Login Formular begnügt sicht mit der Eingabe eines URL.

Dabei wurde das nackte HTML-Formular ein wenig mit CSS schöner gemacht. Um den Login durchzuführen, geben Sie ihre OpenID ein. Sofern Ihre ID gültig ist, also das Format eines URL hat und keine ungültigen Zeichen enthält, werden Sie nun zu der Seite ihres OpenID-Anbieters weitergeleitet, um die Authentifizierung durchzuführen. Schauen Sie sich dazu einmal ihre Identitätsseite an, also das, was Sie als Surfer unter ihrer OpenID zu Gesicht bekommen. Das können Sie bei den meisten Betreibern selbst bestimmen. Wichtig sind hier ein paar Zeilen, denn die bestimmen, wohin die Reise geht.

Das heißt, wenn Sie etwa http://ihrname.ihr_open_id_provider.com besuchen und sich den HTML Code anschauen, sollten Sie in etwa sowas zu Gesicht bekommen:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head >
<title>

http://thiemo-fetzer.myopenid.com/

</title>
<link rel="openid.server" href="http://www.myopenid.com/server" />
<link rel="openid2.provider" href="
http://www.myopenid.com/server" />

In meinem Fall ist der OpenID-Anbieter myOpenID, aber viel wichtiger ist, dass Sie hier erfahren, wo die OpenID-Server für die OpenID-Versionen 1 und 2 ihres OpenID-Anbieters liegen. In dem Authentifizierungsprozess ist das einer der ersten Schritte, da die Anfrage nicht an ihre OpenID gesendet wird, sondern an den OpenID-Server. Die Adresse des OpenID-Servers herauszufinden, findet im ersten Schritt des sogenannten Discovery Prozess statt.

Als Nutzer bekommen Sie davon alles nichts mit – sie landen auf einer Seite, die etwa wie folgt aussehen könnte:

Der Login für die Authentifizierung findet auf der Seite des OpenID Anbieters statt, bei dem ein Nutzer seine OpenID registriert hat. Anhand des Designs kann der Benutzer feststellen, ob er an der richtigen Adresse gelandet ist.

Nach der Authentifizierung, die entweder erfolgreich oder eben nicht erfolgreich ablief, werden Sie auf eine von Ihnen definierten URL weitergeleitet. Dazu aber erst später. Das nackte HTML-Formular samt den ersten paar Zeilen Code sind in folgendem Listing:

openIdAuthentification/index.php
<?php
if (!isset($_POST['submit'])) {
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">
<head>
<title>Mit OpenID anmelden</title>
</head>
<body>
<form method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>">
Personalisieren Sie diese Webseite, indem Sie sich mit Ihrer OpenID <img src="images/openid.gif" alt="OpenID Icon"> anmelden.<br/>
<input type="text" name="id" size="30" />
<br />
<input type="submit" name="submit" value="Einloggen" />
</form>
<?php
} else {
// schauen, ob überhaupt etwas eigegeben wurde
if (trim($_POST['id'] == '')) {
echo "<h1>Geben Sie eine OpenID ein!</h1>";
}
// die Bibliothek einfügen
require_once "Auth/OpenID/Consumer.php";
require_once "Auth/OpenID/FileStore.php";
require_once "Auth/OpenID/SReg.php";
// Sessions erzeugen, mit dieser Session wird später der Nutzer erkannt
session_start();
// Sofern nicht vorhanden, wird ein Flatfilespeicherort für OpenID Authentifizierungen erzeugt
$store = new Auth_OpenID_FileStore('./OpenIdStorage');
// einen neuen OpenID Konsumenten erzeugen
$consumer = new Auth_OpenID_Consumer($store);
// Beginn des Anmeldeprozesses durch Erzeugung einer neuen Anfrage bei dem OpenID Anbieter
$auth = $consumer->begin($_POST['id']);
if (!$auth) {
echo "<h1>Geben Sie eine OpenID ein!</h1>";
}
// Sreg Anfrage erzeugen - also die Anfrage, über die die erweiterten Nutzerdaten zurückgesendet werden sollen
$sreg = Auth_OpenID_SRegRequest::build(array('email', 'fullname', 'dob', 'language'), array('nickname'));
if (!$sreg) {
echo "<h1>Die Nutzerdaten konnten nicht ermittelt werden!</h1>";
}
$auth->addExtension($sreg);
// Weiterleitung auf den OpenID Anbieter zur Authentifizierung
$url = $auth->redirectURL('http://www.devmag.net/openIdAuthentification', 'http://www.devmag.net/openIdAuthentification/OpenIdReturn.php');
header('Location: ' . $url);
}
?></body>
</html>

Dies ist ein Standardbeispiel zur Verwendung der PHPOpenID-Bibliothek. Zunächst wird überprüft, ob bereits über POST eine ID gesendet wurde. Falls dies nicht der Fall ist, wird das Formular angezeigt. Falls doch, so werden zunächst drei Dateien aus der Bibliothek eingefügt. Dabei handelt es sich um die “consumer.php”-Klasse. Diese enthält alle notwendigen Methoden, um eine Authentifizierungsanfrage abzusenden. In der Datei “filestore.php” befinden sich die notwendigen Funktionen, durch welche die OpenID-Sessions und Daten in Flatfiles abgelegt werden. Es gibt, wie bereits erwähnt, auch Bibliotheken für die Verwendung von Datenbanken als Speichermedium, etwa die “MySQLStore.php”. Die dritte Datei wird benötigt, um Anfragen abzusetzen, über welche die einfachen Nutzerdaten ausgetauscht werden.

Zunächst wird daraufhin ein Speicherort für die Flatfiles definiert. Achtung: Hier müssen Schreibrechte gesetzt werden, da das Skript den Ordner, wenn nicht vorhanden, selbst erzeugt. Im nächsten Schritt wird eine Objektinstanz für den neuen Konsumenten, also den Nutzer, der sich authentifiziert, erzeugt. Diesem wird die Speicheradresse (und somit indirekt auch die Methode der Datenspeicherung) übermittelt.

Der eigentliche Authentifizierungsprozess beginnt im nächsten Schritt, wo aus der “consumer.php”-Klasse die Prozedur begin() aufgerufen wird und an diese die OpenID übergeben wird. Die begin()-Prozedur hat die einfache Aufgabe, die URLs, die als OpenIDs angegeben wurden, zu normalisieren. So wird aus “ihrname.myopenid.com” etwa “http://ihrname.myopenid.com”. Auch wird auf nicht zugelassene Sonderzeichen und ähnliches überprüft. Über den Aufruf der Prozedur begin() wird der sogenannte “Discovery-Prozess” eingeleitet. Dabei wird zunächst erkannt, ob es sich bei der OpenID um einen URL handelt oder ein sogenannter XRI Identifier – dabei handelt es sich um eine deutlich kompliziertere Art zur Adressierung von Objekten. Die PHPOpenID Bibliothek unterstützt ab Version 2 sowohl als auch. Darauf soll später noch ein wenig eingegangen werden.

Sollte die Überprüfung erfolgreich verlaufen, so gibt die begin()Prozedur ein Objektinstanz der Klasse Auth_OpenID_AuthRequest zurück. Über diese werden die Anfragen nun versand. Zunächst wird aber noch eine Instanz der Klasse SReg erzeugt, um die Anfrage nach den erweiterten Nutzerdaten mitzusenden.  Diese Anfrage wird über die build()-Funktion erzeugt. Die Anfrage wird daraufhin an unsere Auth_OpenID_AuthRequest-Instanz, welche in $auth liegt, über $auth->addExtension($sreg); hinzugefügt.

Im letzten Teil werden an die Autentifizierungsanfrage noch URLs übergeben. Die erste URL ist die des Dienstes oder der Webseite, welche authentifiziert werden soll. Die zweite URL gibt die Adresse an, auf welche ein Nutzer nach erfolgter Authentifizierung weitergeleitet werden soll.

Die Authentifizierung findet statt - dabei hat der Benutzer die Wahl, welche und ob Informationen an die zu verifizierende Webseite gesendet werden sollen.

Nachdem die Abfrage an den OpenID-Anbieter gesendet wurde, liegt es nun nicht mehr in ihrer Hand. Die Authentifizierung erfolgt auf der Seite des OpenID-Anbieters. Dies stellt wiederum auch ein zusätzliches Sicherheitselement dar, da so der Nutzer anhand des Designs selbst überprüfen kann, ob er hier bei dem richtigen OpenID-Anbieter gelandet ist und nicht etwa bei einem Anbieter, der versucht Daten zu sammeln.

Nach erfolgter Autentifizierung wird der Nutzer nun an die zweite URL weitergeleitet. Jetzt würde uns doch bestimmt heiß interessieren, was für Daten uns der OpenID-Anbieter zurückgesendet hat. Als nächsten Schritt müssen wir uns die Datei OpenIdReturn.php anschauen. Über sie wird die Authentifizierung abgeschlossen, indem die Antwort des OpenID-Anbieters überprüft wird. Im Fall, das die Anmeldung erfolgreich war, können Sie ihren Nutzern Zugriff gewähren.

openIdAuthentification/OpenIdReturn.php

<?php
// Wiederum die passenden Includes
require_once "Auth/OpenID/Consumer.php";
require_once "Auth/OpenID/FileStore.php";
require_once "Auth/OpenID/SReg.php";
// die Session wird gestartet
session_start();
// ein Speicherort für die OpenID Daten erzeugen, sofern noch nicht vorhanden
$store = new Auth_OpenID_FileStore('./OpenIdStorage');
// es wird wiederum ein OpenID Consumer erzeugt über welchen die Antwort des OpenID Anbieters
// über den Status der Anfrage ausgelesen wird
$consumer = new Auth_OpenID_Consumer($store);
$response = $consumer->complete('http://www.devmag.net/openIdAuthentification/OpenIdReturn.php');
// im Fall eines erfolgreichen Logins, wird die Session Variable auf true gesetzt.
// durch eine einfache Überprüfung kann nun auf allen Unterseiten überprüft werden, ob
// diese Session Variable den Wert true hat - der Benutzer also eingeloggt ist
if ($response->status == Auth_OpenID_SUCCESS) {
$_SESSION['OPENID_AUTH'] = true;
// die Informationen, die der OpenID Anbieter übermittelt hat, auslesen
$sreg = new Auth_OpenID_SRegResponse();
$obj = $sreg->fromSuccessResponse($response);
$data = $obj->contents();
echo "<pre>";
var_dump($data);
echo "</pre>";
} else {
$_SESSION['OPENID_AUTH'] = false;
}
?>

Nach erfolgreichem Login können Sie die übermittelten Userdaten anzeigen lassen

Es lohnt sich, den URL einer solchen Anfrage genauer anzuschauen. Über den URL wird schließlich ja dem “Consumer” mitgeteilt, ob der Login erfolgreich verlaufen ist und irgendwie müssen ja auch die Nutzerdaten an unser Script kommen. Der URL einer erfolgreichen Authentifizierung ist erschreckend lang und soll hier auch nicht reproduziert werden. Wer sich jedoch die Mühe macht, mit URLDecode das Kauderweltsch etwas lesbarer zu machen, der erkennt, dass in dem Query String die gesamte Struktur der Anfrage steckt. Dabei finden Sie auch die Daten, die übermittelt werden, samt der Schlüssel, über welche später im Array auf die Daten zugegriffen werden kann sowie die Adressen der Namespace Definitionen der Protokolle OpenID 2.0 sowie der SReg Erweiterung 1.1.

Nachdem Sie die Authentifizierung durchgeführt haben, werden Sie beim nächsten mal nicht mehr nach ihrem Zugang gefragt, sofern Sie noch bei MyOpenID oder ihrem OpenID-Anbieter eingeloggt sind. Dann ist die Seite bereits in der Liste ihrer “Vertrauenswürdigen Seiten” und die Authentifizierungsanfrage wird im Schnelldurchgang durchgeführt, ohne dass Sie zum Beispiel angeben müssen, welche Daten übertragen werden sollen und welche nicht.

Im nächsten Schritt können Sie die Datei OpenIdReturn.php modifizieren, indem Sie ihre Nutzer direkt auf eine andere Seite weiterleiten. Sie können nun prüfen, ob die Nutzer eingeloggt sind, indem Sie schauen, ob die Sessionvariable $_SESSION[‘OPENID_AUTH’] = true; gesetzt ist. Das könnte etwa wie folgt aussehen:

<?php
session_start();
if (!isset($_SESSION
['OPENID_AUTH']) || $_SESSION['OPENID_AUTH'] !== true) {
die ('Der Zugriff auf diese Seite ist
Ihnen nicht gestattet! Bitte melden Sie sich (erneut) an.');
}
?>
<head><title>Kommentar verfassen</title>
</head>

Im zweiten Teil wollen wir versuchen, ob dieser Code auch für eine Authentifizierung mit einer Google OpenID funktioniert – und wie sieht die eigene Google OpenID überhaupt aus? Dieser Teil erscheint morgen. ™

Thiemo Fetzer lebt seit 2008 in London und promoviert dort im Fachbereich "Entwicklungsökonomie" an der London School of Economics. Zuvor hat er Wirtschaftswissenschaften, Mathematik und Informatik in Magdeburg und Ulm studiert.

5 Kommentare zu „OpenID demystified – Teil 1
  1. Michael am 21. September 2009 um 16:14

    Sehr gut geschriebener Artikel!
    Danke

  2. Matthias am 22. September 2009 um 13:30

    Schön geschrieben. Habe selbst schon mit der PHP-Klasse von JanRain gearbeitet und das geht eigentlich gut von der Hand.

  3. […] ersten Teil haben wir die Authentifizierung über OpenID mit der PHPOpenID Klasse durchgespielt. Dabei konnten wir Nutzerdaten über die OpenID Erweiterung SReg abfragen. Diese wird […]

  4. Weihnachtsgrüße | YIID Blog am 11. Januar 2010 um 09:28

    […] das Thema openID noch nicht ganz geheuer ist, dem sei diese tolle Einführung auf Dr. Web ans Herz […]

  5. […] können habe ich mir heute mal die Mühe gemacht, zumindest mal etwas näher draufzuschauen und bin dem -meiner Ansicht nach großartigen- Tutorial auf Dr. Web gefolgt, um mal eine grobe Consumer-Implementierung bei mir Lokal zu […]

Ein Kommentar? Schön!

Wir freuen uns immer über Leser, die durch nützliche und konstruktive Beiträge zum Thema eine Diskussion anstoßen oder den Artikel mit weiteren Informationen anreichern. Alle Kommentare werden in diesem Sinne moderiert. Zum Kommentar-Fairplay gehört für uns auch der Einsatz von rel="nofollow". Bitte verwenden Sie zudem als Namen weder eine Domain noch ein spamverdächtiges Wort. Vielen Dank!