node.js Passport.js 身份验证失败时发回 JSON 响应
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15388206/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Sending back a JSON response when failing Passport.js authentication
提问by kurisukun
I'm using Node.jsas a backend API server for an iPhone client. I'm using Passport.jsto authenticate with a local strategy. The relevant code is below:
我Node.js用作 iPhone 客户端的后端 API 服务器。我使用Passport.js了验证local strategy。相关代码如下:
// 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);
});
});
});
and
和
// 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('/');
});
Right now, I have managed to redirect a failed login to /loginfail, where I send back some JSON to the iPhone client. However, this doesnt have enough granularity. I want to be able to send back the appropriate errors to the iPhone client, such as: "No user found" or "Password is wrong". With my existing code, I don't see how this can be accomplished.
现在,我设法将失败的登录重定向到 /loginfail,在那里我将一些 JSON 发送回 iPhone 客户端。但是,这没有足够的粒度。我希望能够将适当的错误发送回 iPhone 客户端,例如:“找不到用户”或“密码错误”。使用我现有的代码,我看不出这是如何实现的。
I tried to follow the examples for a custom callback on the passport.js site, but I just cant get it to work due to lack of node understanding. How could I modify my code so that I'd be able to send back a res.json with an appropriate error code/message?
我尝试按照passport.js 站点上的自定义回调示例进行操作,但由于缺乏对节点的理解,我无法使其正常工作。我怎样才能修改我的代码,以便我能够发回带有适当错误代码/消息的 res.json?
EDIT: I am trying something like this now:
编辑:我现在正在尝试这样的事情:
// 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);
});
});
});
But back when I try to console.log(info), it just says undefined. I don't know how to get this custom callback working...Any help would be appreciated!
但是当我尝试使用 console.log(info) 时,它只是说未定义。我不知道如何让这个自定义回调工作......任何帮助将不胜感激!
采纳答案by Kevin Dente
I believe the callback function that your 'authenticate' static calls (called 'callback' in your code) accepts a 3rd parameter - "info" - which your code can provide. Then, instead of passing in the { failureRedirect: ...} object, pass in a function which takes 3 arguments - err, user, and info. The "info" you provided in your authenticate method will be passed to this callback.
我相信您的“身份验证”静态调用(在您的代码中称为“回调”)的回调函数接受第三个参数 - “信息” - 您的代码可以提供。然后,不是传入 { failureRedirect: ...} 对象,而是传入一个带有 3 个参数的函数 - err、user 和 info。您在身份验证方法中提供的“信息”将传递给此回调。
Passport calls this scenario "custom callback". See the docs here: http://passportjs.org/guide/authenticate/
Passport 将此场景称为“自定义回调”。请参阅此处的文档:http: //passportjs.org/guide/authenticate/
回答by Mnebuerquo
I had a similar issue with Passportand failed login responses. I was building an API, and wanted all responses to be returned as JSON. Passport responds to an invalid password with status: 401and body: Unauthorized. That's just a text string in the body, not JSON, so it broke my client which expected all JSON.
我遇到了类似的问题Passport并且登录响应失败。我正在构建一个 API,并希望所有响应都以JSON. Passport 以 status:401和 body:响应无效密码Unauthorized。这只是正文中的一个文本字符串,而不是 JSON,因此它破坏了我期望所有 JSON 的客户端。
As it turns out, there is a way to make Passport just return the error to the framework instead of trying to send a response itself.
事实证明,有一种方法可以让 Passport 只将错误返回给框架,而不是尝试发送响应本身。
The answer is to set failWithErrorin the options passed to authenticate:
https://github.com/jaredhanson/passport/issues/126#issuecomment-32333163
答案是failWithError在传递给身份验证的选项中进行设置:https:
//github.com/jaredhanson/passport/issues/126#issuecomment-32333163
From jaredhanson's comment in the issue:
来自 jaredhanson 在该问题中的评论:
app.post('/login',
passport.authenticate('local', { failWithError: true }),
function(req, res, next) {
// handle success
if (req.xhr) { return res.json({ id: req.user.id }); }
return res.redirect('/');
},
function(err, req, res, next) {
// handle error
if (req.xhr) { return res.json(err); }
return res.redirect('/login');
}
);
This will invoke the error handler after Passport calls next(err). For my app, I wrote a generic error handler specific to my use case of just providing a JSON error:
这将在 Passport 调用之后调用错误处理程序next(err)。对于我的应用程序,我编写了一个通用错误处理程序,专门针对我仅提供 JSON 错误的用例:
// 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);
}
Then I used it for all api routes:
然后我将它用于所有 api 路由:
var api = express.Router();
...
//set up some routes here, attached to api
...
// error handling middleware last
api.use( [
handleError
] );
I didn't find the failWithErroroption in the documentation. I stumbled upon it while tracing through the code in the debugger.
我没有failWithError在文档中找到该选项。我在调试器中跟踪代码时偶然发现了它。
Also, before I figured this out, I tried the "custom callback" mentioned in the @Kevin_Dente answer, but it didn't work for me. I'm not sure if that was for an older version of Passport or if I was just doing it wrong.
另外,在我弄清楚这一点之前,我尝试了@Kevin_Dente 答案中提到的“自定义回调”,但它对我不起作用。我不确定这是针对旧版本的 Passport 还是我做错了。
回答by Arthur Araújo
There is an official documentation for Custom Callback:
自定义回调有官方文档:
app.get('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err); }
if (!user) { return res.redirect('/login'); }
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.redirect('/users/' + user.username);
});
})(req, res, next);
});
https://github.com/passport/www.passportjs.org/blob/master/views/docs/authenticate.md
https://github.com/passport/www.passportjs.org/blob/master/views/docs/authenticate.md
回答by RubenJMarrufo
You can do that without custom callbacks using property passReqToCallbackin your strategy definition:
您可以passReqToCallback在策略定义中使用属性在没有自定义回调的情况下做到这一点:
passport.use(new LocalStrategy({passReqToCallback: true}, validateUserPassword));
Then you can add your custom auth error code to the request in your strategy code:
然后您可以在您的策略代码中将您的自定义身份验证错误代码添加到请求中:
var validateUserPassword = function (req, username, password, done) {
userService.findUser(username)
.then(user => {
if (!user) {
req.authError = "UserNotFound";
return done(null, false);
}
And finally you can handle these custom errors in your route:
最后,您可以在您的路线中处理这些自定义错误:
app.post('/login', passport.authenticate('local', { failWithError: true })
function (req, res) {
....
}, function(err, req, res, next) {
if(req.autherror) {
res.status(401).send(req.autherror)
} else {
....
}
}
);
回答by Rahul Gupta
As per the official documentation of Passportyou may use custom callbackfunction to handle the case of failed authorization and override the default message.
根据官方文档,Passport您可以使用自定义回调函数来处理授权失败的情况并覆盖默认消息。
If you are developing REST API and then you would want to send out pretty JSON response something as below:
如果您正在开发 REST API,然后您希望发送漂亮的 JSON 响应,如下所示:
{
"error": {
"name": "JsonWebTokenError",
"message": "invalid signature"
},
"message": "You are not authorized to access this protected resource",
"statusCode": 401,
"data": [],
"success": false
}
I was using Passport JWTauthentication to secure some of my routes and was applied the authMiddlewareas below:
我使用Passport JWT身份验证来保护我的一些路由,并应用authMiddleware如下:
app/middlewares/authMiddleware.js
应用程序/中间件/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
应用程序/路由/approutes.js
const authMiddleware = require('../middlewares/authMiddleware');
module.exports = function (app) {
// secure the route by applying authentication middleware
app.use('/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);
}
});
};
回答by Tal Tikotzki
A short workaround is to emulate the Flash method call which intended originally to support connect-flash and to use this method to return the JSON object.
一个简短的解决方法是模拟最初打算支持连接闪存的 Flash 方法调用,并使用此方法返回 JSON 对象。
first define the "emulator":
首先定义“模拟器”:
var emulateFlash = function (req, res, next) {
req.flash = (type, message) => {
return res.status(403).send({ status: "fail", message });
}
next();
}
this will inject the flash method which will send the error JSON object upon failure.
这将注入 flash 方法,该方法将在失败时发送错误 JSON 对象。
In the route do the following:
在路由中执行以下操作:
1st, use the emulator across the board using:
1st,使用以下方法全面使用模拟器:
router.use(emulateFlash);
One can instead use the emulateFlash method on each route needed.
可以改为在所需的每条路线上使用 emulateFlash 方法。
2nd, on the route when using authenticate, specify the failureFlash option using a message:
2、在使用身份验证的路由上,使用消息指定 failureFlash 选项:
router.route("/signin")
.post(.authenticate('local', { session: false, failureFlash: "Invalid email or password."}), UsersController.signIn);
I tested this for both failed authentication as well as successful and found it working. Looking at the code I could not find any other way to return an object other than implementing the callback method which requires much more work.
我对失败的身份验证和成功的身份验证都进行了测试,发现它可以正常工作。查看代码,除了实现需要更多工作的回调方法之外,我找不到任何其他方法来返回对象。

