Authentifizierung in Express mit Passport.js-Strategien meistern
Takashi Yamamoto
Infrastructure Engineer · Leapcell

Einführung
In der heutigen vernetzten digitalen Landschaft benötigt fast jede Webanwendung ein robustes und sicheres Authentifizierungssystem. Von der Sicherung sensibler Benutzerdaten bis zur Personalisierung des Benutzererlebnisses ist eine zuverlässige Authentifizierung das Fundament der modernen Anwendungsentwicklung. Der Aufbau eines Authentifizierungssystems von Grund auf kann jedoch eine entmutigende Aufgabe sein, die mit Sicherheitspannen und komplexen Implementierungsdetails verbunden ist. Hier kommt Passport.js ins Spiel. Passport.js ist die De-facto-Authentifizierungs-Middleware für Node.js und bietet einen flexiblen und modularen Ansatz zur Authentifizierung von Anfragen. Es bietet eine saubere API, die es Entwicklern ermöglicht, verschiedene Authentifizierungsstrategien zu "plug-in", die von traditionellen Benutzername/Passwort-Kombinationen bis hin zu modernen Token-basierten und sozialen Logins reichen. Dieser Artikel befasst sich mit den Feinheiten von Passport.js und untersucht, wie lokale, JSON Web Token (JWT) und gängige Social-Login-Strategien in einer Express.js-Anwendung implementiert werden, um Ihnen das Wissen und die Werkzeuge an die Hand zu geben, um sichere und skalierbare Authentifizierung in Ihren Projekten zu erstellen.
Kernkonzepte von Passport.js
Bevor wir mit der Implementierung beginnen, wollen wir ein gemeinsames Verständnis der Kernkonzepte entwickeln, die für Passport.js zentral sind:
- Strategien: Im Herzen von Passport.js stehen "Strategien". Eine Strategie ist ein in sich geschlossenes Modul, das eine bestimmte Art von Authentifizierung handhabt. So verarbeitet
passport-local
die Benutzername/Passwort-Authentifizierung,passport-jwt
die JWT-Verifizierung undpassport-google-oauth20
den Google-Login. Jede Strategie wird mit spezifischen Optionen konfiguriert und implementiert eineverify
-Funktion. - Verify-Funktion: Dies ist der wichtigste Teil jeder Passport.js-Strategie. Die
verify
-Funktion ist dafür verantwortlich, einen Benutzer anhand der von der Strategie bereitgestellten Anmeldedaten zu finden (z. B. Benutzername und Passwort für die lokale Strategie, Token für die JWT-Strategie, Profilinformationen für soziale Strategien). Wenn ein Benutzer gefunden und authentifiziert wird, ruft sie einedone
-Callback-Funktion mit dem Benutzerobjekt auf. Wenn die Authentifizierung fehlschlägt, ruft siedone
mitfalse
oder einem Fehler auf. - Serialisierung/Deserialisierung: Passport.js wird oft mit Sitzungen integriert. Wenn ein Benutzer erfolgreich authentifiziert wurde, muss Passport.js in der Lage sein, minimale Benutzerinformationen in der Sitzung zu speichern, um ihn bei nachfolgenden Anfragen zu identifizieren. Dies wird durch die Funktionen
serializeUser
unddeserializeUser
gehandhabt.serializeUser
bestimmt, welche Benutzerdaten in der Sitzung gespeichert werden sollen, unddeserializeUser
ruft das vollständige Benutzerobjekt anhand dieser gespeicherten Daten aus der Datenbank ab. Dieser Prozess ermöglicht es, nachfolgende Anfragen zu authentifizieren, ohne dass der Benutzer seine Anmeldedaten erneut eingeben muss.
Implementierung von Authentifizierungsstrategien
Lassen Sie uns nun untersuchen, wie verschiedene Authentifizierungsstrategien in eine Express.js-Anwendung integriert werden.
Einrichtung der grundlegenden Express-Anwendung
Stellen Sie zunächst sicher, dass Sie eine Express-Anwendung eingerichtet haben.
// app.js const express = require('express'); const session = require('express-session'); const passport = require('passport'); const bcrypt = require('bcryptjs'); // Für Hashing von Passwörtern der lokalen Strategie const app = express(); app.use(express.json()); app.use(express.urlencoded({ extended: true })); // Konfigurieren Sie die Sitzungs-Middleware app.use(session({ secret: 'ein sehr geheimes Schlüssel für die Sitzung', // Ersetzen Sie dies durch einen starken, zufälligen Schlüssel resave: false, saveUninitialized: false, cookie: { maxAge: 24 * 60 * 60 * 1000 } // 24 Stunden })); // Initialisieren Sie Passport.js app.use(passport.initialize()); app.use(passport.session()); // Nur erforderlich, wenn Sie Sitzungen verwenden // Dummy-Benutzerdatenbank zur Demonstration const users = []; // Passport-Serialisierung/Deserialisierung (erforderlich für sitzungsbasierte Authentifizierung) passport.serializeUser((user, done) => { done(null, user.id); }); passport.deserializeUser((id, done) => { const user = users.find(u => u.id === id); done(null, user); }); // Grundlegende Route zum Prüfen, ob authentifiziert app.get('/profile', (req, res) => { if (req.isAuthenticated()) { res.send(`Willkommen, ${req.user.username}! `); } else { res.status(401).send('Nicht authentifiziert'); } }); const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log(`Server läuft auf Port ${PORT}`); });
1. Lokale Strategie (Benutzername/Passwort)
Die lokale Strategie ist die grundlegendste Authentifizierungsmethode, die auf einem Benutzernamen und Passwort basiert, die in Ihrer Datenbank gespeichert sind.
Installation:
npm install passport-local --save
Implementierung:
// app.js (zum bestehenden app.js hinzufügen) const LocalStrategy = require('passport-local').Strategy; // Registriere einen Dummy-Benutzer zum Testen bcrypt.hash('password123', 10, (err, hash) => { if (err) throw err; users.push({ id: '1', username: 'testuser', password: hash }); }); passport.use(new LocalStrategy( async (username, password, done) => { try { const user = users.find(u => u.username === username); if (!user) { return done(null, false, { message: 'Falscher Benutzername.' }); } const isMatch = await bcrypt.compare(password, user.password); if (!isMatch) { return done(null, false, { message: 'Falsches Passwort.' }); } return done(null, user); } catch (err) { return done(err); } } )); // Lokale Login-Route app.post('/login', passport.authenticate('local', { successRedirect: '/profile', failureRedirect: '/login', // Hier würden Sie typischerweise ein Login-Formular rendern failureFlash: true // Benötigt connect-flash Middleware })); app.get('/logout', (req, res) => { req.logout((err) => { if (err) { return next(err); } res.redirect('/login'); // Nach dem Logout zur Login-Seite umleiten }); });
Erklärung:
- Wir initialisieren
LocalStrategy
und stellen eineverify
-Funktion bereit. - Die
verify
-Funktion nimmtusername
,password
und einedone
-Callback-Funktion entgegen. - Innerhalb von
verify
suchen wir den Benutzer in unseremusers
-Array (in einer echten App wäre dies eine Datenbankabfrage). - Wenn der Benutzer nicht gefunden wird oder das Passwort nach Verwendung von
bcrypt.compare
nicht übereinstimmt, wirddone(null, false, { message: ... })
aufgerufen, um einen Authentifizierungsfehler anzuzeigen. - Wenn die Anmeldedaten gültig sind, wird
done(null, user)
aufgerufen, wobei das authentifizierte Benutzerobjekt übergeben wird. - Die POST-Route
/login
verwendetpassport.authenticate('local', ...)
, um die Strategie auszulösen.successRedirect
undfailureRedirect
steuern, wohin der Benutzer nach der Authentifizierung weitergeleitet wird.
2. JWT-Strategie (Token-basiert)
JWT (JSON Web Token) Authentifizierung wird häufig für zustandslose APIs verwendet. Anstelle von Sitzungen sendet ein Server dem Client nach erfolgreichem Login einen Token, den der Client dann in nachfolgenden Anfragen zur Authentifizierung mitführt.
Installation:
npm install passport-jwt jsonwebtoken --save
Implementierung:
// app.js (zum bestehenden app.js hinzufügen, stellen Sie sicher, dass auch die jwt-Abhängigkeit hinzugefügt wird) const JwtStrategy = require('passport-jwt').Strategy; const ExtractJwt = require('passport-jwt').ExtractJwt; const jwt = require('jsonwebtoken'); const JWT_SECRET = 'ihr_jwt_geheimnis'; // Ersetzen Sie dies durch einen starken, zufälligen Schlüssel // JWT-Strategie-Optionen const jwtOptions = { jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), secretOrKey: JWT_SECRET }; passport.use(new JwtStrategy(jwtOptions, async (jwt_payload, done) => { try { const user = users.find(u => u.id === jwt_payload.sub); // 'sub' ist Standard für Benutzer-ID if (user) { return done(null, user); } else { return done(null, false); } } catch (err) { return done(err, false); } })); // Route zum Generieren von JWT (z. B. nach erfolgreichem lokalen Login) app.post('/api/login-jwt', async (req, res, next) => { passport.authenticate('local', { session: false }, (err, user, info) => { if (err || !user) { return res.status(401).json({ message: info ? info.message : 'Login fehlgeschlagen' }); } req.login(user, { session: false }, (err) => { if (err) res.send(err); const token = jwt.sign({ sub: user.id, username: user.username }, JWT_SECRET, { expiresIn: '1h' }); return res.json({ user, token }); }); })(req, res, next); }); // Geschützte JWT-Route app.get('/api/protected', passport.authenticate('jwt', { session: false }), (req, res) => { res.json({ message: `Willkommen ${req.user.username} zur geschützten JWT-Route!`, user: req.user }); });
Erklärung:
- Wir definieren
jwtOptions
, um der Strategie mitzuteilen, wo das JWT gefunden werden soll (z. B. imAuthorization
-Header als Bearer-Token) und welcher Schlüssel zur Verifizierung verwendet werden soll. - Die
verify
-Funktion derJwtStrategy
empfängt die dekodierte JWT-Payload (jwt_payload
) und diedone
-Callback-Funktion. - Sie verwendet dann
jwt_payload.sub
(typischerweise die Benutzer-ID), um den Benutzer in der Datenbank zu finden. - Wenn gefunden, wird
done(null, user)
aufgerufen. Andernfalls wirddone(null, false)
aufgerufen. - Die Route
/api/login-jwt
verwendet zuerst die lokale Strategie zur Authentifizierung der Anmeldedaten. Wenn erfolgreich, generiert sie ein JWT mitjsonwebtoken.sign
und sendet es an den Client zurück. - Die Route
/api/protected
verwendetpassport.authenticate('jwt', { session: false })
, um die Route zu schützen.session: false
ist hier entscheidend, da JWT zustandslos ist und keine serverseitige Sitzungsspeicherung benötigt.
3. Social Login (Google OAuth 2.0 Beispiel)
Social Login ermöglicht es Benutzern, sich über ihre vorhandenen Konten von Plattformen wie Google, Facebook oder GitHub zu authentifizieren. Dies verbessert die Benutzererfahrung und reduziert die Hürden bei der Registrierung.
Installation:
npm install passport-google-oauth20 --save
Einrichtung des Google API-Projekts:
- Gehen Sie zur Google Cloud Console.
- Erstellen Sie ein neues Projekt.
- Navigieren Sie zu
APIs & Services > Credentials
. - Klicken Sie auf
+ Create Credentials
, wählen SieOAuth-Client-ID
. - Wählen Sie
Webanwendung
. - Legen Sie
autorisierte JavaScript-Ursprünge
auf die URL Ihrer App fest (z. B.http://localhost:3000
). - Legen Sie
autorisierte Weiterleitungs-URIs
auf Ihre Callback-URL fest (z. B.http://localhost:3000/auth/google/callback
). - Sie erhalten eine
client ID
und einclient secret
.
Implementierung:
// app.js (zum bestehenden app.js hinzufügen) const GoogleStrategy = require('passport-google-oauth20').Strategy; const GOOGLE_CLIENT_ID = 'IHRE_GOOGLE_CLIENT_ID'; // Ersetzen Sie dies durch Ihre tatsächliche Client-ID const GOOGLE_CLIENT_SECRET = 'IHR_GOOGLE_CLIENT_SECRET'; // Ersetzen Sie dies durch Ihr tatsächliches Client-Secret passport.use(new GoogleStrategy({ clientID: GOOGLE_CLIENT_ID, clientSecret: GOOGLE_CLIENT_SECRET, callbackURL: "/auth/google/callback" // Dies muss mit der Weiterleitungs-URI Ihrer Google Cloud Console übereinstimmen }, async (accessToken, refreshToken, profile, done) => { try { // In einer echten Anwendung würden Sie den Benutzer in Ihrer Datenbank speichern/finden // basierend auf profile.id oder profile.emails[0].value let user = users.find(u => u.googleId === profile.id); if (!user) { // Wenn der Benutzer nicht existiert, erstellen Sie einen neuen const newUser = { id: profile.id, // Verwendung von googleId als unsere primäre ID zur Vereinfachung googleId: profile.id, username: profile.displayName, email: profile.emails && profile.emails[0] ? profile.emails[0].value : null, // Möglicherweise möchten Sie mehr Profildaten speichern }; users.push(newUser); user = newUser; } return done(null, user); } catch (err) { return done(err, null); } })); // Google-Authentifizierungsrouten app.get('/auth/google', passport.authenticate('google', { scope: ['profile', 'email'] }) ); app.get('/auth/google/callback', passport.authenticate('google', { failureRedirect: '/login' }), (req, res) => { // Erfolgreiche Authentifizierung, weiter zur Startseite. res.redirect('/profile'); } );
Erklärung:
- Wir konfigurieren
GoogleStrategy
mitclientID
,clientSecret
undcallbackURL
. - Die
verify
-Funktion für Google nimmtaccessToken
,refreshToken
,profile
(enthält Benutzerdaten von Google) unddone
entgegen. - Innerhalb von
verify
prüfen wir, ob ein Benutzer mit derprofile.id
(Google-ID) bereits in unsererusers
-Datenbank vorhanden ist. - Wenn nicht, wird ein neuer Benutzer erstellt und hinzugefügt.
- Nach erfolgreicher Authentifizierung wird
done(null, user)
aufgerufen, um den Vorgang abzuschließen. - Die Route
/auth/google
leitet den Benutzer zur Authentifizierung zu Google weiter und gibt denscope
der genehmigten Berechtigungen an. - Nach erfolgreicher Authentifizierung auf Googles Seite leitet Google den Benutzer zurück zu
/auth/google/callback
. Passport.js fängt diesen Callback ab, verarbeitet die Google-Antwort und ruft dieverify
-Funktion der Strategie auf. - Nach erfolgreicher Verifizierung wird der Benutzer zu
/profile
weitergeleitet.
Anwendungsszenarien
- Lokale Strategie: Ideal für traditionelle Webanwendungen, bei denen Benutzer ihre Konten direkt in Ihrem System verwalten. Geeignet für interne Tools oder Anwendungen, die eine strenge Kontrolle über Benutzerdaten erfordern.
- JWT-Strategie: Am besten geeignet für APIs, mobile Anwendungen und Single-Page Applications (SPAs), bei denen ein zustandloser Ansatz bevorzugt wird. Ermöglicht eine skalierbare Authentifizierung ohne serverseitige Sitzungsspeicherung.
- Social Login: Verbessert das Benutzererlebnis durch Bequemlichkeit und reduziert die Hürden bei der Anmeldung. Besonders nützlich für verbraucherorientierte Anwendungen, bei denen Benutzer vorhandene Konten bevorzugen.
Es ist üblich, diese Strategien zu kombinieren. Zum Beispiel könnte sich ein Benutzer zunächst mit E-Mail/Passwort (lokale Strategie) anmelden und später sein Google-Konto verknüpfen. Oder eine Webanwendung verwendet lokale Authentifizierung für allgemeine Benutzer, legt aber eine API offen, die durch JWT für mobile Clients geschützt ist.
Fazit
Passport.js ist ein unverzichtbares Werkzeug für JavaScript-Entwickler, die Authentifizierung in ihre Express-Anwendungen integrieren. Durch das Verständnis seiner modularen, strategie-basierten Architektur können Sie eine Vielzahl von Authentifizierungsmechanismen nahtlos integrieren, von traditionellen lokalen Logins bis hin zu modernen Token-basierten und sozialen Authentifizierungsflüssen. Die Flexibilität und Erweiterbarkeit von Passport.js befähigt Entwickler, sichere, robuste und benutzerfreundliche Authentifizierungssysteme zu erstellen, die auf die spezifischen Bedürfnisse ihrer Anwendungen zugeschnitten sind und eine solide Grundlage für jedes Webprojekt bilden. Die Beherrschung von Passport.js ist ein grundlegender Schritt zur Entwicklung sicherer und skalierbarer benutzerzentrierter Anwendungen.