2013-03-13 143 views
40

对不起,我对node.js非常陌生,所以我还没有能够绕过这个包裹。我使用Node.js作为iPhone客户端的后端API服务器。我使用Passport.js进行本地策略认证。相关代码如下:发送失败时发送JSON响应Passport.js身份验证

// 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); 
     }); 
    }); 
}); 

// 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('/'); 
}); 

现在,我已成功地重定向失败的登录/ loginfail,在那里我一些JSON发送回iPhone客户端。但是,这没有足够的粒度。我希望能够将适当的错误发送回iPhone客户端,例如:“找不到用户”或“密码错误”。用我现有的代码,我不明白这是如何实现的。

我试图按照passport.js站点上的自定义回调的示例,但由于缺乏节点理解,我无法使其工作。我怎样才能修改我的代码,以便能够发送res.json和适当的错误代码/消息?

编辑:我现在想是这样的:

// 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); 
     }); 
    }); 
}); 

但回来时,我尝试CONSOLE.LOG(信息),它只是说不确定。我不知道如何得到这个自定义回调工作...任何帮助将不胜感激!

回答

19

我相信你的'认证'静态调用(在你的代码中称为“回调”)的回调函数接受第三个参数 - “信息” - 你的代码可以提供。然后,不要传入{failureRedirect:...}对象,而是传入一个需要3个参数的函数 - err,user和info。您在验证方法中提供的“信息”将传递给此回调。

护照将此场景称为“自定义回调”。看到文档在这里: http://passportjs.org/guide/authenticate/

+0

感谢你为这个。我知道自定义回调,但我的问题是我不明白如何实现它(对于节点和js非常抱歉)。我用我的最新努力编辑了我的问题。正如你所看到的,我试图使用这个“info”参数,但我不知道如何正确使用它 - 当我从身份验证返回时它总是未定义的。 – kurisukun 2013-03-14 02:53:18

+0

对不起,我刚才发现我还需要添加“info”以及: passport.use(新的LocalStrategy(函数(用户名,密码,完成)){User.authenticate(username,password,function(err ,user,info){ return done(err,user,info); }); } )); – kurisukun 2013-03-14 02:55:20

+0

这次对话使我得出了一个更具实质性的结论。 https://github.com/jaredhanson/passport/issues/255 – jpierson 2016-01-11 20:43:52

42

我有一个类似的问题,Passport和失败的登录响应。我正在构建一个API,并希望所有响应都以JSON的形式返回。 Passport响应一个无效的密码,状态为:401,body:“未经授权”。这只是一个正文中的文本字符串,而不是JSON,所以它打破了我的客户端,期望所有的JSON。

事实证明,有一种方法可以让Passport将错误返回给框架,而不是尝试发送响应本身。

答案是设置failWithError在通过验证的选项: https://github.com/jaredhanson/passport/issues/126#issuecomment-32333163

从问题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'); 
    } 
); 

这将调用错误处理程序调用护照后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); 
} 

然后我用了所有的API路线:

var api = express.Router(); 
... 
//set up some routes here, attached to api 
... 
// error handling middleware last 
api.use([ 
     handleError 
     ]); 

我没找到文档中的failWithError选项。我在调试器中追踪代码时偶然发现了它。

另外,在我想出来之前,我尝试了@Kevin_Dente答案中提到的“自定义回调”,但它对我无效。我不确定这是否适用于较早版本的护照,或者我只是做错了。

+4

这仍然适用于2016年。谢谢! – iPhoney 2016-04-30 14:01:54

+2

2017年仍有效LOL – 2017-04-18 00:21:26

+1

仍有效。 :P – 2017-07-19 07:44:54

1

没有为自定义回调的官方文档:

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

1

你可以做,没有在你的策略定义使用属性 “passReqToCallback” 定制回调:

passport.use(new LocalStrategy({passReqToCallback: true}, validateUserPassword)); 

然后,您可以将您的自定义身份验证错误代码添加到策略公司的请求中德:

var validateUserPassword = function (req, username, password, done) { 
    userService.findUser(username) 
     .then(user => { 
      if (!user) { 
       req.authError = "UserNotFound"; 
       return done(null, false); 
      } 

最后,你可以处理你的路线这些自定义错误:

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 { 
      .... 
     } 
    } 
);