패쓰포트에 대해 이해해보자!

김민주·2022년 5월 15일
0

클라이언트와 서버는 서로 노관심이다.
따라서 둘을 이어주는 cookie와 session이 필요하다.

쿠키와 세션이 클라이언트와 서버에서 둘을 어떻게 이어주는 지 알아보자.
1. 브라우저(클라이언트)에서 서버로 request를 보내면,
2. 서버는 데이터베이스에 세션이라는 것을 저장한다.
3. 반면 서버는 클라이언트에게 response를 보낼 때, cookie라는 것과 함께 돌려보낸다.
4. 이 쿠키는 위에서 말한 데이터베이스를 참조하며, 이 쿠키에게는 session id라는 것이 존재한다.
5. 이 쿠키는 서버로 보내는 모든 request와 항상 함께 보내지고, 이를 통해 서버는 사용자가 누구인지 판별할 수가 있다. (이것이 stateless한 HTTP를 stateful하게 바꿔주는 것)

그렇다. 위 과정은 매우 복잡하다.


코드로 살펴보자.

app.js 파일에서 아래와 같이 적어주고,

const session = require("express-session");

app.use(session({
  secret: 'secretkey',
  resave: false,
  saveUninitialized: false,
  store: store,
}));

const authRouter = require("./routes/auth");

app.use("/auth", authRouter);

routes>auth.js 파일에서

router.get("/", (req, res, next) => {
  console.log(req.session);
  console.log(req.session.id);
});

콘솔에는 req.session에는 Session { cookie : { path:"/", _expires: null, originalMaxAge: null, httpOnly: true } } 이 찍힌다.
Session 안에 cookie라는 객체가 담긴다.
req.session.id에는 0tpqLxtQosg7kjSKIrHKuc9lHI-hyhZH 이런식으로 이상하게 생긴 외계어 담긴다.


브라우저에서 살펴보자.
개발자도구를 살펴서 쿠키를 살펴보면 app.js파일에서 saveUninitialized를 false로 해놨기 때문에, 변화가 없을 때는 쿠키가 담기지 않는다. 따라서

router.get('/login', function(req, res, next) {
  req.session.isAuth = true;
  res.render('login', { title: 'Login Page' });
});

와 같이 아무런 곳에 req.session.isAuth라는 것을 생성해주어 변화를 주면 개발자도구에서 쿠키가 생성된 것을 볼 수 있다.

아래 Cookie Value 중 긴 문자열 중 0tpqLxtQosg7kjSKIrHKuc9lHI-hyhZH 인 session.id가 포함되어 있다는 것을 알 수 있다.


오케이, 쿠키와 세션은 이해가 되었다. 로그인 기능을 구현할 때,

이렇게 그냥 req.session.isAuth가 존재하는지(true)인지, 존재하지 않는지에 따라 권한을 부여해주면 될 것 같다. 로그아웃 기능은 req.session.destroy()를 해주면 되고!

라고 생각을 하였는데, 그럼에도 불구하고 패쓰포트가 존재하는 이유를 찾아보던 중 다음과 같은 글을 읽었다.

However, right now the session object is not storing any important information. That is where passport comes in to take advantage of this functionality for implementing user authentication.

세션은 어떠한 중요한 정보도 저장해주지 못한다는 것이다.
중요한 정보란 무엇을 의미하는가????


패쓰포트의 장점: 소셜 로그인, 로직 간소화? 맞나?


Facebook 또는 Twitter와 같은 외부 서비스를 사용할 때, 사용자의 프로필 정보를 가져와서 인코딩하는 방식이 각각 다릅니다.

따라서 서비스 간의 통합을 보다 쉽게 하기 위해 Passport는 프로필 정보를 정규화시켜줍니다.

Passport는 Node.js에서 Authentication[인증]을 하기 위하여 사용되는 미들웨어입니다. express외 여러 곳에서 사용이 가능합니다.

app.use(passport.initialize())
이렇게 app.js파일 내부에 적어주면 이제 passport는 사용가능한 미들웨어가 됩니다!

app.post('/login/password', passport.authenticate('local'));

패쓰포트는 위 문장 하나로 끝!

위 문장에는 총 3가지 개념이 들어있습니다.
1. 미들웨어
2. 전략(Strategy)
3. 세션

1. 미들웨어

  app.post('/login/password',
  	passport.authenticate('local',	{ failureRedirect: '/login', failureMessage: true }),
  			function(req, res) {
    			res.redirect('/~' + req.user.username);
  			}
  );

여기에서 passport.authenticate()는 미들웨어로, 디폴트로 authentication이 성공했을 때 req.user가 인증이 된 유저한테 새로이 생겨납니다. 그리고 session도 그 다음에 생겨납니다.

2. 전략

유저의 credential한 정보들(비밀번호, 카드 번호)을 어떻게 인코딩할지를 정해줍니다. 만약 성공적으로 verify를 마치면, 인증이 된 것입니다.

대표적인 passport-local의 전략

const LocalStrategy = require('passport-local');
const strategy = new LocalStrategy(function verify(username, password, cb) {
  db.get('SELECT * FROM users WHERE username = ?', [ username ], function(err, user) {
    if (err) { return cb(err); }
    if (!user) { return cb(null, false, { message: 'Incorrect username or password.' }); }
    crypto.pbkdf2(password, user.salt, 310000, 32, 'sha256', function(err, hashedPassword) {
      if (err) { return cb(err); }
      if (!crypto.timingSafeEqual(user.hashed_password, hashedPassword)) {
        return cb(null, false, { message: 'Incorrect username or password.' });
      }
      return cb(null, user);
    });
  });
});

이 verify함수가 user를 찾으면 return cb(null, user)을 반환합니다.
user가 없거나 잘못된 정보를 입력하면 return cb(null, false)를 반환합니다.
에러가 발생하면 return cb(err)를 반환합니다.

strategy config 이후

const passport = require('passport');
passport.use('password', strategy);

app.js 파일에서 passport를 use 선언해주어야 합니다. use안에 첫번째 인자로 들어가는 값이 strategy의 이름인데, local같은 경우는 'local'이라고 지어주는 반면, 이름을 새로이 지정해주고 싶으면 'password' 이렇게 재지정시켜줄 수 있습니다.

profile
성공은 퍼포먼스가 아니라 지속성이다. 언제 이루어지는지가 아니라, 어떤 모양으로 이루는지가 더 중요하다.

0개의 댓글