OpenID in Rails einbinden
Das Integrieren von OpenID in eine Rails-Anwendung wird einem ziemlich leicht gemacht: Wer es weniger flexibel, dafür aber umso schneller fertig haben möchte, für den eignet sich das OpenID Consumer Plugin. Wer allerdings lieber selbst wissen möchte, was wie funktioniert, der kann das OpenID Login auch in wenigen Schritten selbst implementieren.
1. Installieren der Library
Für den Umgang mit OpenID (sowohl Consumer als auch Sever) gibt es bereits das Ruby-OpenID Gem. Da wir dessen Funktionalität nutzen wollen, muss es zunächst installiert werden. Einfach über die Konsole
$ sudo gem install ruby-openid -y
eingeben. Dadurch werden das Gem und die nötigen Abhängigkeiten (-y
ist die Abkürzung für --include-dependencies
) lokal installiert.
2. Der OpenID Controller
Um die nötigen Funktionen zu implementieren brauchen wir nur einen Controller und vier dazugehörige Actions. Um die OpenID-Funktionalität vom Rest der Anwendung zu trennen, empfiehlt es sich, einen neuen Controller zu generieren:
./script/generate controller Openid login begin complete openid_consumer
Der Befehl erzeugt uns den OpenidController
mit den Actions login
, begin
, complete
und openid_consumer
, welche wir nacheinander implementieren werden. Bevor es damit allerdings losgehen kann, muss in Zeile 1 des neuen Controllers das in Schritt 1 installierte Gem eingebunden werden:
require_gem 'ruby-openid'
Damit stehen uns im Controller alle Funktionen des Gems bereit, welche wir für die Implementierung der Actions nutzen wollen. Der Controller dürfte bis dahin so aussehen:
require_gem 'ruby-openid' class OpenidController < ApplicationController def login end def begin end def complete end def openid_consumer end end
3. openid_consumer
Wir fangen mit der Action openid_consumer
an und markieren sie als protected
, da sie nur Controller-intern verwendet werden wird und daher kein Zugriff über die URL erlaubt werden soll.
protected def openid_consumer @openid_consumer ||= OpenID::Consumer.new(session, OpenID::FilesystemStore.new("#{RAILS_ROOT}/tmp/openid")) end
Der Einzeiler erstellt eine Openid-Consumer Instanz: Das erste Argument muss ein Hash-Objekt sein, welches die Sessiondaten speichert (in Rails also session
) und der zweite Parameter legt fest, wo die Daten des Verifizierungsprozesses abgelegt werden sollen. In diesem Fall nehmen wir das Dateisystem - es ist allerdings auch möglich, die Datenbank zu nutzen (das in der Einleitung erwähnte Plugin macht es über Active Record).
4. login
In der Action login
braucht nichts weiter passieren, es geht lediglich darum, den View mit dem Formular bereitzustellen, welches im einfachsten Falle irgendwie so aussieht:
<% form_tag url_for(:action => :begin) do %> <%= text_field_tag 'openid_url' %> <%= submit_tag "mit OpenID einloggen" %> <% end -%>
Laut Konvention sollte man immer das OpenID-Logo als Hintergrundbild für das Loginfeld einbinden, was sich mit folgendem CSS leicht tun lässt:
#openid_url { background: url(/images/login-bg.gif) no-repeat #FFF 5px; padding-left: 25px; }
5. begin
Nächster Schritt ist die Action begin
, welche die aus dem Formular abgeschickten Anfragen annimmt und verarbeitet.
def begin openid = params[:openid_url] response = openid_consumer.begin openid if response.status == OpenID::SUCCESS # Daten anfordern - siehe Spec: # http://openid.net/specs/openid-simple-registration-extension-1_0.html response.add_extension_arg('sreg','required','nickname,email') response.add_extension_arg('sreg','optional','fullname') # Request senden - erster Parameter = Trusted Site, # zweiter Parameter = anschließende Weiterleitung redirect_to response.redirect_url(home_url, openid_complete_url) else flash[:error] = "Der OpenID-Server #{openid} war nicht erreichbar." redirect_to url_for(:action => :login) end end
Im Falle eines erfolgreichen Response des vom User angegebenen OpenID-Servers wird ein Request mit der Anforderung der Userdaten gesendet. In diesem Fall werden die Daten nickname
und email
zwingend angefordert, fullname
ist optional. Darüber hinaus gibt es noch weitere Daten, welche man über das OpenID-Login beziehen kann, eine genaue Auflistung findet sich in der Spezifikation des Response-Formats.
Der Request zum Abschließen der Verifikation wird mit zwei Parametern an den OpenID-Server geschickt: Beim ersten Parameter handelt es sich um die URL der Website, welcher der User die Daten zur Verfügung stellt (also die Basis-URL unserer Anwendung - in diesem Fall über benannte Routes die home_url
. Der zweite Parameter legt fest, wo die anschließende Verarbeitung des Request stattfinden soll, also in unserer complete
Action (wäre ohne benannte Routes url_for(:controller => :openid, :action => :complete)
).
6. complete
Hier nun also der letzte Schritt, in welchem die Antwort mit der OpenID-Verifizierung verarbeitet wird.
def complete response = openid_consumer.complete params openid = response.identity_url if response.status == OpenID::SUCCESS # Hier muss das Login implementiert werden, # also Session befüllen oder sonstige Aktionen. # Die Daten stehen als # params["openid.sreg.nickname"] # etc. zur Verfügung end else flash[:error] = "Das Login mit der OpenID #{openid} war nicht erfolgreich." redirect_to login_url end end
Wie das letztendliche Login aussieht (Eintragen in Session, Erstellen eines Benutzerkontos oder ähnliches), hängt von der Anwendung ab und muss im auskommentierten Teil selbst implementiert werden. Die vom OpenID-Server gelieferten Daten des Users stehen über params["openid.sreg.nickname"]
bereit.
Anmerkungen
Beim Schreiben des Tutorials habe ich mich an Dan Webbs No Shit Guide To Supporting OpenID In Your Applications orientiert. Mittlerweile gibt es auch vom Rails Core Team das OpenID Authentication Plugin, welches auch als Wrapper für die Funktionen des Ruby-OpenID Gems dient.