64

Node.jsiPhoneクライアントのバックエンド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);
        });
    });
});

// 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(info)を実行しようとすると、undefinedと表示されます。このカスタムコールバックを機能させる方法がわかりません...助けていただければ幸いです。

4

6 に答える 6

74

Passportログイン応答で同様の問題が発生し、失敗しました。私はAPIを構築していて、すべての応答をとして返されるようにしたかったのJSONです。Passportは、ステータス:401および本文:で無効なパスワードに応答しますUnauthorizedこれは本文内の単なるテキスト文字列であり、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');
  }
);

これにより、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);
}

次に、すべてのAPIルートに使用しました。

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

failWithErrorドキュメントにオプションが見つかりませんでした。デバッガーでコードをトレースしているときに、それを見つけました。

また、これを理解する前に、@ Kevin_Denteの回答に記載されている「カスタムコールバック」を試しましたが、うまくいきませんでした。それが古いバージョンのPassportの場合なのか、それとも間違っていたのかはわかりません。

于 2016-01-09T21:32:27.603 に答える
31

「認証」静的呼び出し(コードでは「コールバック」と呼ばれます)が、コードが提供できる3番目のパラメーター「info」を受け入れるコールバック関数だと思います。次に、{failureRedirect:...}オブジェクトを渡す代わりに、err、user、infoの3つの引数を取る関数を渡します。認証メソッドで指定した「情報」は、このコールバックに渡されます。

Passportは、このシナリオを「カスタムコールバック」と呼んでいます。こちらのドキュメントを参照してください:http: //passportjs.org/guide/authenticate/

于 2013-03-13T22:07:32.470 に答える
9

カスタムコールバックの公式ドキュメントがあります:

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

于 2017-10-29T12:57:41.810 に答える
2

の公式ドキュメントによると、カスタムコールバックPassport関数を使用して、承認に失敗した場合を処理し、デフォルトのメッセージを上書きすることができます。

REST APIを開発していて、次のようなきれいなJSON応答を送信したい場合:

{
    "error": {
        "name": "JsonWebTokenError",
        "message": "invalid signature"
    },
    "message": "You are not authorized to access this protected resource",
    "statusCode": 401,
    "data": [],
    "success": false
}

Passport JWT一部のルートを保護するために認証を使用しておりauthMiddleware、次のように適用されました。

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 / routers / 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);
    }
  });
};
于 2019-06-24T04:48:34.847 に答える
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 {
            ....
        }
    }
);
于 2018-02-20T16:44:50.723 に答える
0

簡単な回避策は、もともとconnect-flashをサポートすることを目的としたFlashメソッド呼び出しをエミュレートし、このメソッドを使用してJSONオブジェクトを返すことです。

最初に「エミュレータ」を定義します。

var emulateFlash = function (req, res, next) {
        req.flash = (type, message) => {
            return res.status(403).send({ status: "fail", message });
        }
        next();
    }

これにより、失敗時にエラーJSONオブジェクトを送信するflashメソッドが挿入されます。

ルートで次のことを行います。

まず、以下を使用してエミュレータを全面的に使用します。

router.use(emulateFlash);

代わりに、必要な各ルートでemulateFlashメソッドを使用できます。

2番目に、認証を使用する場合のルートで、メッセージを使用してfailureFlashオプションを指定します。

router.route("/signin")
    .post(.authenticate('local', { session: false,  failureFlash: "Invalid email or password."}), UsersController.signIn);

認証の失敗と成功の両方についてこれをテストし、機能していることを確認しました。コードを見ると、はるかに多くの作業を必要とするコールバックメソッドを実装する以外に、オブジェクトを返す他の方法を見つけることができませんでした。

于 2020-04-19T21:12:57.850 に答える