JUINTINATION

Express.js와 passport-local을 사용한 로그인 테스트 본문

StudyNote

Express.js와 passport-local을 사용한 로그인 테스트

DEOKJAE KWON 2024. 1. 21. 21:38
반응형

passport란?

이전 Express.js의 morgan과 cookie-parser, express-session 글에서 쿠키와 세션을 이용해 클라이언트를 식별하는 방법에 대해 다뤘다. passport는 내부적으로 쿠키와 세션을 사용하여 Node.js에서 인증(Authenicate)을 쉽게 적용할 수 있게 하는 미들웨어이다. 해외로 나갈 때 신원 확인을 위한 여권(passport)를 사용하는 것처럼 클라이언트가 서버에 요청을 보낼 수 있는지 심사하는 역할이라고 보면 된다.

strategy

passport는 어떤 것을 이용해 어떻게 인증을 구현할 것인가에 대한 전략인 strategy를 사용하는데 오늘을 기준으로 539가지 strategy가 있다고 공식 홈페이지에 나와 있다. 예를 들어 'passport-kakao'를 사용한다고 하면 카카오 계정을 통해 인증을 수행할 수 있는 것이다. 우리가 흔히 사용하는 소셜 로그인 외에도 OpenID, Heroku, HTTP Bearer 등 다양한 인증 방법이 있다. 서비스의 목적에 맞게 사용하면 되는데 보통의 웹 서비스는 주로 지역에서 구현하는 'passport-local'를 사용하게 된다. 이번에 나는 테스트를 위해 'passport-local'을 사용할 것이다. 이를 위해 $ npm install passport passport-local 커맨드를 실행하여 모듈을 설치하자.

app.js

const express = require('express');
const http = require('http');
const path = require('path');
const logger = require('morgan');
const cookieParser = require('cookie-parser');
const session = require('express-session');
const passport = require('passport');
const Localstrategy = require('passport-local').Strategy;

const app = express();
app.set('port', process.env.PORT || 3000);

app.use(express.json());
app.use(express.urlencoded({ extended: true })); // POST 데이터 파싱

app.use(logger('dev'));

app.use(cookieParser('passport-test'));
app.use(session({
  secret: 'passport-test',
  resave: false,
  saveUninitialized: false,
  cookie: {
    httpOnly: true,
    secure: false
  }
}));

/* 가상 데이터 */
let fakeUser = {
  username: 'test',
  password: '1234'
};

/* passport 미들웨어 */
app.use(passport.initialize());
app.use(passport.session());

// 세션 처리 - 로그인에 성공했을 경우 딱 한번 호출되어 사용자의 식별자를 session에 저장
passport.serializeUser((user, done) => {
  console.log('serializeUser', user);
  done(null, user.username);
});

// 세션 처리 - 로그인 후 페이지 방문마다 사용자의 실제 데이터 주입
passport.deserializeUser((id, done) => {
  console.log('deserializeUser', id);
  done(null, fakeUser);
});

// passport-local Strategy 사용
passport.use(new Localstrategy((username, password, done) => {
    if (username === fakeUser.username) {
      if (password === fakeUser.password) {
        return done(null, fakeUser);
      } else {
        return done(null, false, { message: "password incorrect" });
      }
    } else {
      return done(null, false, { message: "username incorrect" });
    }
  }
));

const indexRouter = require('./routes/index');
app.use('/', indexRouter);

const userRouter = require('./routes/users');
app.use('/user', userRouter);

// login
app.post('/login', passport.authenticate('local', { failureRedirect: '/'}), (req, res) => {
  res.redirect('/');
});

// logout
app.get("/logout", (req, res, next) => {
	req.logout(() => {
		res.redirect('/');
	})
});

app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

app.use(express.static(__dirname + '/public'));

/* 404 오류 처리 */
app.use((req, res, next) => {
  const error = new Error(`${req.method} ${req.url} 해당 주소가 없습니다.`)
  error.status = 404;
  next(error);
});

/* 오류 처리 미들웨어 */
app.use((err, req, res, next) => {
  res.locals.message = err.message;
  res.locals.error = process.env.NODE_ENV !== 'development' ? err : {};
  res.status(err.status || 500);
  res.send('error Occurred');
});

http.createServer(app).listen(app.get('port'), () => {
  console.log('Express server listening on port ' + app.get('port'));
});

시간이 없어서 간단하게 설명하자면 fakeUser를 만들어서 해당 유저와의 정보를 비교하는 것이다. 코드에 간단하게 주석 처리를 해두었으니 해석이 어렵진 않을 것이다.

나는 로그인이 아예 안 되고 자꾸 아무런 오류도 출력되지 않고 redirection 돼서 한참을 헤맸는데 Post 데이터를 파싱해주는 app.use(express.urlencoded({ extended: true })); 가 없어서 그랬던 것 같다. 이 글을 읽는 중인데 해당 코드가 없다면 꼭 추가해주자..

그리고 로그아웃 기능도 안 됐는데 찾아보니 req.logout()  함수 안에 콜백 함수가 필요하다고 한다. 그래서 기존의 코드였던 아래 코드로 계속 해봤는데 안 됐던 것이다. 즉, 아래 코드는 잘못된 코드이다.

// logout
app.get("/logout", (req, res, next) => {
  req.logout();
  res.redirect('/');
});

240122 수정

더 찾아보니 다음 코드를 사용하면 콜백 함수를 쓰지 않아도 로그아웃 기능이 정상적으로 실행된다.

// logout
app.get("/logout", (req, res, next) => {
  req.session.destroy();
  res.redirect('/');
});

아무튼 프로그램을 실행하고 처음 localhost:3000에 접속하면 다음과 같이 뜬다.

여기에 fakeUser의 정보인 test와 1234를 비밀번호로 입력하면 다음과 같이 뜬다.

로그아웃 버튼을 누르면 처음에 봤던 초기화면으로 넘어가진다.

routes/index.js

var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', (req, res) => {
  if (!req.user) {
    res.render('index', { title: 'Welcome page' });
  } else {
    const user = req.user.username;
    res.render('user_page', { user: user });
  }
});

module.exports = router;

views/index.jade

doctype html
html(lang='en')
  head
    meta(charset='UTF-8')
    meta(name='viewport', content='width=device-width, initial-scale=1.0')
    link(rel="stylesheet", href="/stylesheets/style.css")
    title #{title}
  body
    form(action='/login', method='post')
      div
        label(for='username') Username:
        input(type='text', name='username')
      div
        label(for='password') Password:
        input(type='password', name='password')
      div
        input(type='submit', value='Log In')

views/user_page.jade

doctype html
html(lang='en')
  head
    meta(charset='UTF-8')
    meta(name='viewport', content='width=device-width, initial-scale=1.0')
    link(rel="stylesheet", href="/stylesheets/style.css")
    title User Page
  body
    p #{user}님 안녕하세요!
    button(type='button', onclick="location.href='/logout'") Log Out

결론

이번엔 passport의 strategy 중 하나인 passport-local을 이용하여 간단한 로그인 테스트를 진행했다. 처음에 로그인이 왜 안 되는지 디버깅하는 과정에서 app.use(express.json()) 뿐만 아니라 app.use(express.urlencoded({ extended: true }))도 같이 사용해야 한다는 것을 알게 되면서 허무하지만 기쁜.. 뭐 그런 오묘한 감정이 들었다. 이렇게 하나하나 필수적인 코드를 추가해는 과정이 즐겁기도 하지만 나는 아직 멀었구나 라는 생각이 동시에 들기도 하는 그런 하루인 것 같다.

728x90
Comments