쿠키와 세션, passport 전략

KyungminLee·2021년 2월 23일
1

쿠키와세션, JWT

목록 보기
1/3
post-thumbnail
post-custom-banner

Passport 전략

노드 라이브러리 패스포트는 유저네임/비밀번호를 이용한 인증 뿐만 아니라 페북/트위터 소셜 인증, jwt 인증 등 방법이 무척 많다. 공통의 인증 로직은 passport가 담당하고 구체적인 방법은 "전략"이라는 개념으로 분리해 놓았다.

  • 유저네임/비밀번호 인증은 passport-local
  • 페북/트위터는 passport-facebook, passport-twitter
  • jwt 방식은 passport-jwt

이번에 포스팅할 전략은 유저네임/비밀번호 전략이다.

passport-local

유저네임/비밀번호를 이용한 "로컬 전략"이 사용한 인증 수단이다. passport-local 패키지를 사용해서 진행한다.

routes/user.js

const passport = require('passport');
const { Post, User } = require('../../models');

router.post('/login', (req, res, next) => {
  //미들웨어 확장
  //여기서 passport전략으로 간다.
  passport.authenticate('local', (err, user, info) => {
    if (err) {
      console.error(err);
      return next(err);
    }
    if (info) {
      return res.status(401).send(info.reason);
    }
    //여기서 serializeUser가 실행
    return req.login(user, async (loginErr) => {
      if (loginErr) {
        console.error(loginErr);
        return next(loginErr);
      }
      const fullUserWithoutPassword = await User.findOne({
        where: { id: user.id },
        attributes: {
          exclude: ['password'],
        },
        include: [
          {
            model: Post,
          },
          { model: User, as: 'Followings' },
          { model: User, as: 'Followers' },
        ],
      });
      console.log(fullUserWithoutPassword);
      return res.status(200).json(fullUserWithoutPassword);
    });
  })(req, res, next);
});

클라이언트 서버로부터 post요청이 왔을 시 passport.authenticate 를 통해 passport전략을 수행한다. 처리되는 내용은 아래 파일과 같다.

passport/local.js

onst passport = require('passport');
const { Strategy: LocalStrategy } = require('passport-local');
const { User } = require('../../models');
const bcrypt = require('bcrypt');

module.exports = () => {
  passport.use(
    new LocalStrategy(
      {
        usernameField: 'email',
        passwordField: 'password',
      },
      async (email, password, done) => {
        try {
          const user = await User.findOne({
            //이메일 검사
            where: { email },
          });
          if (!user) {
            //이메일 없으면?
            return done(null, false, { reason: '존재하지 않은 이메일입니다!!' }); //서버에러, 성공, 클라이언트에러
          }
          const result = await bcrypt.compare(password, user.password); //이메일이 존재한다면 비밀번호 비교
          if (result) {
            return done(null, user);
          }
          return done(null, false, { reason: '비밀번호가 틀렸습니다' });
        } catch (error) {
          console.error(error);
          return done(error);
        }
      },
    ),
  );
};
  • 위 코드에서 Strategy 은 옵션 객체와 인증 정보로 사용자를 찾는 콜백함수를 받는다.
  • done에는 세개의 파라메터를 넘길 수 있는데 첫번째인자가 서버에러, 두번째인자가 성공, 세번째인자가 클라이언트 에러이다.
  • 제대로 처리 된다면 return done(null, user)을 통해 User db에서 찾은 정보가 넘어간다.

다시 routes/user.js를 확인해보면 return req.login(user, async (loginErr) => 이 부분에서 serializeUser가 실행이 된다. 아래 passport/index.js코드를 확인해보자.

passport/index.js

const passport = require('passport');
const local = require('./local');
const { User } = require('../../models');

module.exports = () => {
  passport.serializeUser((user, done) => {
    done(null, user.id); //세션에 유저 id만저장
  });
  //로그인 성공 후 그 다음 요청부터 deserializeUser가 실행
  passport.deserializeUser(async (id, done) => {
    try {
      const user = await User.findOne({ where: { id } }); //메모리에 저장된 id를 찾아서 가져온다.
      done(null, user); //req.user에 저장.
    } catch (error) {
      console.log(error);
      done(null, error);
    }
  });

  local();
};

여기서 serializeUserdeserializeUser 가 등장한다.

  • serizliseUser : passport는 세션에 최소한의 인증 정보만 저장한다. 로그인 한 뒤 세션에 데이터를 저장할때 어떤 정보를 저장할지를 결정하는 함수가 serizliseUser이다.
  • deserializeUser : 반대로 세션에 저장한 데이터로 로그인한 유저 정보를 복구하는데 이걸 결정하는 함수가 deserializeUser다.

[추가설명]

  • serialize는 직렬화, deserialize는 역직렬화이다.

  • 직렬화라는 것은 어떤 데이터를 다른 곳에서 사용할 수 있게 다른 포맷의 데이터로 바꾸는 것을 의미한다. 지금 패스포트에서는 시퀄라이즈 객체를 세션에 저장할 수 있는 데이터로 바꾸고 있다.

  • 반대로 역직렬화는 다른 포맷의 데이터로 바뀐 데이터를 원래 포맷으로 복구하는 것이다. 세션에 저장된 데이터를 다시 시퀄라이즈 객체로 바꾸는 작업을 의미한다.

  • deserialize에서 복구된 값은 req.body가 아니라 req.user에 들어간다. 세션쿠키를 통해서 메모리에 저장된 id를 찾아서 가져오는 것이다.

  • 일단 passport.session이 deserializeUser을 실행하지만. deserializeUser는 serializeUser 후에 모든 라우터 접근 시 실행된다.


[브라우저 네트워크 확인]
아래 사진과 같이 로그인을 진행했을 때 네트워크 탭에서 헤더에 세션id가 포함 된 쿠키가 들어온다는 것을 확인할 수 있다.

profile
끊임없이 발전해가는 개발자.
post-custom-banner

0개의 댓글