Nodejs-Tutorial Teil 5: Echte Authentifizierung

Posted by in JavaScript, Nodejs

In Teil 4 wurde ein naiver Login erstellt, um zu zeigen, wie man aus der Datenbank MongoDB Daten auslesen kann. In diesem Teil geht es darum, den Code zu einem echten Authentifizierungsverfahren auszubauen. Dazu brauchen wir Passport, das die Referenzimplementierung für diese Aufgabe bereitstellt.

Vorbemerkungen

Während der Arbeit an der Authentifizierung, bin ich an die Grenzen des Machbaren mit Monk und meiner Architektur gestoßen und habe mich deshalb entschlossen eine MVC-Architektur zu verwenden in Verbindung mit Mongoose. Das Ganze hab ich nach dem Vorbild von diesem Blog gemacht.

Modifizierung package.json

Die package.json muss um die Abhängigkeiten zu Passport und Mongoose ergänzt werden. Monk fällt weg. Außerdem haben wir noch ein Verschlüsselungspackage bcrypt-nodejs hinzugefügt, um die Passwörter zu verschlüsseln.

Dann im Verzeichnis application wieder die Installation anstoßen mit

Modifikation app.js

In der app.js gab es einige Umbaumaßnahmen für die neue Architektur. Monk wurde durch Mongoose ersetzt. Es bleibt aber bei der Initialisierung sonst ziemlich ähnlich. Die Passport-Konfiguration wird in der Datei passport.js im Ordner config abgelegt und dient der Loginsteuerung. Dann müssen wir natürlich auch unserer app noch sagen, dass Passport verwendet wird. Unsere Router müssen durch den Umbau auch noch anders initialisiert werden, denn für eine Authentifizierung braucht die Route das Passport-Objekt und für das Routing wird nicht mehr der Express-Router benutzt sondern direkt über das app-Objekt geroutet (warum, weiß ich ehrlich gesagt nicht… anders hat es nicht funktioniert 😉 ).

Modifikation index.js

Unser Router exportiert jetzt eine Funktion mit zwei Argumenten, damit der Aufruf aus der app.js auch funktioniert. Statt router.get/post verwenden wir jetzt app.get/post. Die Route dologin sorgt dafür, dass der User zuerst mit Passport authentifiziert wird und zwar mit der Strategie local-login. Bei Erfolg wird auf die Route loginuser weitergeleitet, ansonsten bekommt der User wieder die Loginmaske angezeigt. Bleibt noch die Funktion isLoggedIn, die nur überprüft ob der User für einen Request authentifiziert ist. Man kann sie praktisch bei jeder Route verwenden, indem sie bei der entsprechenden Route zwischengeschaltet wird.

Usermodel

Unser User wird durch ein Mongoose-Schema auf der usercollection unserer Datenbank definiert, das dann als Mongoose-Model exportiert wird. Unser userSchema greift auf die zwei Attribute local.username und local.password unseres DB-Eintrages zu. Diesen Zusammenhang zu verstehen hat mich ein bisschen Zeit gekostet, denn ich habe nicht verstanden, warum meine Passport-Authentifizierung, die später erläutert wird, einfach nicht funktioniert hat (konnte ja nicht gehen, wenn es local gar nicht als eingebettetes Element gibt seufz). Dem Model werden noch zwei Methoden hinzugefügt. Die erste generateHash wird später für die Registrierung gebraucht. Die zweite validPassword wird gebraucht, um das eingegebene Passwort aus dem Login-Formular zu verifizieren.

Natürlich muss die Datenbank auch angepasst werden. Das erledigen wir auf der MongoDB-Shell mit einem Update

Login mit Passport

Kommen wir endlich zur eigentlichen Authentifizierung mit Passport. Dies passiert in der passport.js im Ordner config.

In Passport arbeitet man mit sogenannten Strategies. Wir benutzen nur den lokalen Login, aber es gibt auch alles erdenklich andere wie Social-Networks. Für die Session De-/Serialisierung braucht man die gleichnamigen Funktionen User.findById in serializeUser greift auf das vorher erstellte Usermodel zu und führt eine Datenbankabfrage nach der id durch.
Die wichtigste Logik definiert die local-login-Authentifizierung über username und password. Diese beiden Variablen werden direkt auf die Input-Felder aus dem Loginformular gemappt. Hier hatte ich einen Schreibfehler und die Authentifizierung wollte und wollte nicht funktionieren 😉 .
Ist alles korrekt gemappt, dann wird die Authentifizierungsfunktion aufgerufen. Dort wird zuerst wieder über unser Usermodel überprüft, ob überhaupt ein Benutzer mit diesem Namen existiert. Hier lauert ein weiterer Stolperstein, denn nur mit User.findOne(..) kann ein gefundener User aus der Datenbank auf das Model gemappt werden. Benutzt man nur User.find(..) dann fehlt die Methode validPassword, da der User ein normaler JSON-String wäre. Geht alles glatt wird, das Request an die aufrufende Route zurück gegeben (siehe unsere index.js).

Fazit und Ausblick

Die ganzen Umbaumaßnahmen führten zu einer MVC-Architektur, die sich auf dieser Basis leichter erweitern lässt. Wir haben jetzt ein einfaches Usermodel, das sich um weitere Authentifizierungsformen mit Passport erweitern lässt. Dafür war es notwendig die Standardroute für ‚/‘ umzubauen, dass bei ihrer Initialisierung passport mitübergeben werden konnte. Weiter benutzt unsere Applikation nicht mehr Monk sondern Mongoose als Datenbankmiddleware. Ich finde Mongoose zu recht die Standard-Library für diese Aufgabe.
Im nächsten Teil des Tutorials wird gezeigt, wie man es den Benutzern ermöglicht sich über ein Formular zu registrieren. Das sollte inzwischen einfacher gehen, als noch vor diesem Teil 😉