Zurücksenden einer JSON-Antwort, wenn die Passport.js-Authentifizierung fehlschlägt
Lesezeit: 9 Minuten
kurisukun
Ich benutze Node.js als Backend-API-Server für einen iPhone-Client. Ich benutze Passport.js sich authentifizieren mit a local strategy. Der entsprechende Code ist unten:
// This is in user.js, my user model
UserSchema.static('authenticate', function(username, password, callback) {
this.findOne({ username: username }, function(err, user) {
if (err){
console.log('findOne error occurred');
return callback(err);
}
if (!user){
return callback(null, false);
}
user.verifyPassword(password, function(err, passwordCorrect){
if (err){
console.log('verifyPassword error occurred');
return callback(err);
}
if (!passwordCorrect){
console.log('Wrong password');
return callback(err, false);
}
console.log('User Found, returning user');
return callback(null, user);
});
});
});
und
// This is in app.js
app.get('/loginfail', function(req, res){
res.json(403, {message: 'Invalid username/password'});
});
app.post('/login',
passport.authenticate('local', { failureRedirect: '/loginfail', failureFlash: false }),
function(req, res) {
res.redirect("https://stackoverflow.com/");
});
Im Moment ist es mir gelungen, eine fehlgeschlagene Anmeldung an /loginfail umzuleiten, wo ich etwas JSON an den iPhone-Client zurücksende. Dies hat jedoch nicht genug Granularität. Ich möchte in der Lage sein, die entsprechenden Fehler an den iPhone-Client zurückzusenden, z. B.: “Kein Benutzer gefunden” oder “Passwort ist falsch”. Mit meinem vorhandenen Code sehe ich nicht, wie dies erreicht werden kann.
Ich habe versucht, den Beispielen für einen benutzerdefinierten Rückruf auf der Website „passport.js“ zu folgen, aber ich kann es einfach nicht zum Laufen bringen, weil ich die Knoten nicht verstehe. Wie könnte ich meinen Code so ändern, dass ich eine res.json mit einem entsprechenden Fehlercode/einer entsprechenden Fehlermeldung zurücksenden kann?
Ich versuche jetzt so etwas:
// In app.js
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err) }
if (!user) {
console.log(info);
// *** Display message without using flash option
// re-render the login form with a message
return res.redirect('/login');
}
console.log('got user');
return res.json(200, {user_id: user._id});
})(req, res, next);
});
// In user.js
UserSchema.static('authenticate', function(username, password, callback) {
this.findOne({ username: username }, function(err, user) {
if (err){
console.log('findOne error occurred');
return callback(err);
}
if (!user){
return callback(null, false);
}
user.verifyPassword(password, function(err, passwordCorrect){
if (err){
return callback(err);
}
if (!passwordCorrect){
return callback(err, false, {message: 'bad password'});
}
console.log('User Found, returning user');
return callback(null, user);
});
});
});
Aber wenn ich es damals mit console.log(info) versuche, sagt es nur undefiniert. Ich weiß nicht, wie ich diesen benutzerdefinierten Rückruf zum Laufen bekomme … Jede Hilfe wäre willkommen!
Ich stolperte auch in diesem Problem für eine Weile. Ihre Lösung ist ähnlich wie benutzerdefinierter Rückruf aus Passport.js doc
– FisNaN
8. November 2018 um 2:51 Uhr
benutzerdefinierter Rückruf macht es zu Ihrer Verantwortung, eine Sitzung einzurichten. Wenn Sie dies vermeiden möchten, habe ich eine Problemumgehung gefunden, indem ich den Flash-Nachrichtenmechanismus emuliert habe (siehe unten).
– Tal Tikotzki
27. April 2020 um 12:28 Uhr
Mnebuerquo
Ich hatte ein ähnliches Problem mit Passport und fehlgeschlagene Login-Antworten. Ich habe eine API erstellt und wollte, dass alle Antworten als zurückgegeben werden JSON. Passport antwortet auf ein ungültiges Passwort mit folgendem Status: 401 und Körper: Unauthorized. Das ist nur eine Textzeichenfolge im Körper, kein JSON, also hat es meinen Client kaputt gemacht, der alles JSON erwartet hat.
Wie sich herausstellt, gibt es eine Möglichkeit, Passport dazu zu bringen, den Fehler einfach an das Framework zurückzugeben, anstatt zu versuchen, selbst eine Antwort zu senden.
Dadurch wird die Fehlerbehandlungsroutine nach Passport-Aufrufen aufgerufen next(err). Für meine App habe ich einen generischen Fehlerhandler geschrieben, der speziell auf meinen Anwendungsfall zugeschnitten ist, bei dem nur ein JSON-Fehler bereitgestellt wird:
// Middleware error handler for json response
function handleError(err,req,res,next){
var output = {
error: {
name: err.name,
message: err.message,
text: err.toString()
}
};
var statusCode = err.status || 500;
res.status(statusCode).json(output);
}
Dann habe ich es für alle API-Routen verwendet:
var api = express.Router();
...
//set up some routes here, attached to api
...
// error handling middleware last
api.use( [
handleError
] );
Ich habe die nicht gefunden failWithError Option in der Dokumentation. Ich bin darauf gestoßen, als ich den Code im Debugger durchgegangen bin.
Bevor ich das herausfand, habe ich auch den in der @Kevin_Dente-Antwort erwähnten “benutzerdefinierten Rückruf” ausprobiert, aber er hat bei mir nicht funktioniert. Ich bin mir nicht sicher, ob das für eine ältere Version von Passport war oder ob ich es einfach falsch gemacht habe.
noch gültig in 2017 LOL
– Edwin O.
18. April 2017 um 0:21 Uhr
noch gültig. 😛
– Toan Tran
19. Juli 2017 um 7:44 Uhr
2021 noch gültig
– AndroConsis
9. Dezember 2021 um 9:21 Uhr
Ich glaube, dass die Callback-Funktion, die Ihre statischen Aufrufe “authentifizieren” (in Ihrem Code als “Callback” bezeichnet) einen dritten Parameter – “info” – akzeptiert, den Ihr Code bereitstellen kann. Anstatt das Objekt { failureRedirect: …} zu übergeben, übergeben Sie dann eine Funktion, die 3 Argumente akzeptiert – err, user und info. Die “Informationen”, die Sie in Ihrer Authentifizierungsmethode angegeben haben, werden an diesen Rückruf weitergeleitet.
Danke dafür. Ich bin mir des benutzerdefinierten Rückrufs bewusst, aber mein Problem ist, dass ich einfach nicht verstehe, wie ich ihn implementieren soll (sorry, sehr neu in node und js). Ich habe meine Frage mit meinen neuesten Bemühungen bearbeitet. Wie Sie sehen, versuche ich, diesen “info”-Parameter zu verwenden, aber ich weiß nicht, wie ich ihn richtig verwenden soll – er ist immer undefiniert, wenn ich von der Authentifizierung zurückkomme.
– kurisukun
14. März 2013 um 2:53 Uhr
Tut mir leid, ich habe gerade herausgefunden, dass ich auch “info” hinzufügen muss: Passport.use(new LocalStrategy(function(username, password, done) { User.authenticate(username, password, function(err, user, info ) { return done(err, user, info); }); } ));
Gemäß der offiziellen Dokumentation von Passport Sie können verwenden benutzerdefinierter Rückruf Funktion, um den Fall einer fehlgeschlagenen Autorisierung zu behandeln und die Standardnachricht zu überschreiben.
Wenn Sie eine REST-API entwickeln und dann eine hübsche JSON-Antwort wie folgt senden möchten:
{
"error": {
"name": "JsonWebTokenError",
"message": "invalid signature"
},
"message": "You are not authorized to access this protected resource",
"statusCode": 401,
"data": [],
"success": false
}
Ich benutzte Passport JWT Authentifizierung zur Sicherung einiger meiner Routen und wurde angewendet authMiddleware wie nachstehend:
app/middlewares/authMiddleware.js
const express = require('express');
const router = express.Router();
const passport = require('passport');
const _ = require('lodash');
router.all('*', function (req, res, next) {
passport.authenticate('local', function(err, user, info) {
// If authentication failed, `user` will be set to false. If an exception occurred, `err` will be set.
if (err || !user || _.isEmpty(user)) {
// PASS THE ERROR OBJECT TO THE NEXT ROUTE i.e THE APP'S COMMON ERROR HANDLING MIDDLEWARE
return next(info);
} else {
return next();
}
})(req, res, next);
});
module.exports = router;
app/routes/approutes.js
const authMiddleware = require('../middlewares/authMiddleware');
module.exports = function (app) {
// secure the route by applying authentication middleware
app.use("https://stackoverflow.com/users", authMiddleware);
.....
...
..
// ERROR-HANDLING MIDDLEWARE FOR SENDING ERROR RESPONSES TO MAINTAIN A CONSISTENT FORMAT
app.use((err, req, res, next) => {
let responseStatusCode = 500;
let responseObj = {
success: false,
data: [],
error: err,
message: 'There was some internal server error',
};
// IF THERE WAS SOME ERROR THROWN BY PREVIOUS REQUEST
if (!_.isNil(err)) {
// IF THE ERROR IS REALTED TO JWT AUTHENTICATE, SET STATUS CODE TO 401 AND SET A CUSTOM MESSAGE FOR UNAUTHORIZED
if (err.name === 'JsonWebTokenError') {
responseStatusCode = 401;
responseObj.message="You are not authorized to access this protected resource";
}
}
if (!res.headersSent) {
res.status(responseStatusCode).json(responseObj);
}
});
};
RubenJMarrufo
Sie können dies ohne benutzerdefinierte Rückrufe mit der Eigenschaft tun passReqToCallback in Ihrer Strategiedefinition:
Eine kurze Problemumgehung besteht darin, den Flash-Methodenaufruf zu emulieren, der ursprünglich Connect-Flash unterstützen sollte, und diese Methode zu verwenden, um das JSON-Objekt zurückzugeben.
Ich habe dies sowohl auf fehlgeschlagene als auch auf erfolgreiche Authentifizierung getestet und festgestellt, dass es funktioniert. Beim Betrachten des Codes konnte ich keine andere Möglichkeit finden, ein Objekt zurückzugeben, als die Callback-Methode zu implementieren, die viel mehr Arbeit erfordert.
10494900cookie-checkZurücksenden einer JSON-Antwort, wenn die Passport.js-Authentifizierung fehlschlägtyes
Ich stolperte auch in diesem Problem für eine Weile. Ihre Lösung ist ähnlich wie benutzerdefinierter Rückruf aus Passport.js doc
– FisNaN
8. November 2018 um 2:51 Uhr
benutzerdefinierter Rückruf macht es zu Ihrer Verantwortung, eine Sitzung einzurichten. Wenn Sie dies vermeiden möchten, habe ich eine Problemumgehung gefunden, indem ich den Flash-Nachrichtenmechanismus emuliert habe (siehe unten).
– Tal Tikotzki
27. April 2020 um 12:28 Uhr