IT이야기

Passport.js 인증 실패시 JSON 응답을 다시 보내기

cyworld 2021. 4. 15. 21:49
반응형

Passport.js 인증 실패시 JSON 응답을 다시 보내기


내가 사용하고 Node.js아이폰 클라이언트를위한 백엔드 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라고 표시됩니다. 이 사용자 지정 콜백을 작동시키는 방법을 모르겠습니다 ... 어떤 도움을 주시면 감사하겠습니다!


정적 호출을 '인증'하는 콜백 함수 (코드에서 '콜백'이라고 함)는 코드에서 제공 할 수있는 세 번째 매개 변수 인 "info"를 허용합니다. 그런 다음 {failureRedirect : ...} 객체를 전달하는 대신 err, user 및 info의 3 개 인수를받는 함수를 전달합니다. 인증 방법에 제공 한 "정보"가이 콜백으로 전달됩니다.

Passport는이 시나리오를 "사용자 지정 콜백"이라고합니다. http://passportjs.org/guide/authenticate/ 에서 문서를 참조하십시오.


비슷한 문제가 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 용인지 또는 내가 잘못한 것인지 잘 모르겠습니다.


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


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

의 공식 문서에 따라 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 / routes / 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);
    }
  });
};

ReferenceURL : https://stackoverflow.com/questions/15388206/sending-back-a-json-response-when-failing-passport-js-authentication

반응형