본문 바로가기

Dot Programming/Node.js

[Node.js] Passport로 로그인 구현하기

    passport는 node.js 미들웨어로서 사용자 인증을 구현해준다. 페이스북, 카카오 등 다양한 passport가 있지만 일단은 기본인 이메일을 통한 로그인을 구현해보려 한다. (check)

     

    Documentation: Facebook

    Facebook The Facebook strategy allows users to log in to a web application using their Facebook account. Internally, Facebook authentication works using OAuth 2.0. Support for Facebook is implemented by the passport-facebook module. Install $ npm install p

    www.passportjs.org

     

    1. passport 설치

    $ npm i passport passport-local

     

    2. passport 세팅

    passport 폴더 생성 후 index.js 파일(여기에 passport 설정) 생성

    serializeUser, deserializeUser 이 두개가 passport를 설정하는 기능이고, local은 따로 

    const passport = require('passport');
    const local = require('./local');
    
    module.exports = () => {
        passport.serializeUser(() => {
    
        });
    
        passport.deserializeUser(() => {
    
        });
    
        local();
    };

    passport/local.js 파일 생성

    const 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 : 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의 이름을 LocalStrategy로 설정하는 이유는 나중에 구글이나 카카오 로그인을 구현할 때 구분을 쉽게하기 위해서이다.

     

    로컬 로그인 전략

    1. 객체 부분 : req body부분을 설정해준다.

    usernameField: 'email', passwordField: 'password'  (req body에 담긴 이름 그대로 써주면 됨 id면 id)

    2. 함수 부분: 객체에서 받은 인자들을 그대로 갖고와서 사용한다 (email, password, done) 

    3. done (a, b, c) 

    a: 서버 에러, b: 성공 여부, c: 클라이언트 에러

     

     

    3. passport 설정 적용

    app.js

    const passportConfig = require('./passport');
    passportConfig();

     

    4. login api에 적용

    routes/user.js

    const passport = require('passport');
    
    
    router.post('/login', passport.authenticate('local', (err, user, info) => {
            if(err){
                console.error(err);
                next(err);
            }
    
    });
    

    LocalStrategy done이 콜백함수의 역할을 하여서 각 인자 값들을 리턴한다.

    ex) passport.authenticate('local' , (err, user, info))  //해당 변수 이름은 자유롭게 설정해도 됨

    • err : 서버 에러
    • user : 성공했을시 데이터 or false
    • info : 클라이언트 에러

    → 하지만 위와 같이하면 req, res, next를 사용할 수 없기 때문에 미들웨어를 확장(express 기법)해준다.

     

    미들웨어 확장

    router.post('/login', (req,res, next)=> {
        passport.authenticate('local', (err, user, info) => {
            if(err){
                console.error(err);
                return next(err);
            }
            if(info){
                return res.status(401).send(info.reason);
            }
            
            // passport.login
            return req.login(user, async(loginErr) => {
                if(loginErr){
                    console.error(loginErr);
                    return next(loginErr);
                }
    
                return res.json(user);
            })
        })(req, res, next);
    });

    서버와 클라에러가 없고 passport login을 거치면 로그인 완료!

    이젠 json객체를 프론트에게 전달해주면 된다

     

     

    +++++++++++++++++++++++++++++

     

    로그인을 했으면 로그인한 정보를 저장해줘야한다. 

    백서버에서는 유저 정보를 세션에 들고있고 프론트에서는 쿠키로 유저정보를 담아서 보내준다.

    (보안 취약을 보완하기 위한 jwt토큰이나 여러 방식이 있지만 지금은 그냥 패스)

    $ npm i express-session
    $ npm i cookie-parser

    app.js

    const session = require('express-session');
    const cookieParser = require('cookie-parser');
    const passport = require('passport');
    
    
    app.use(cookieParser('secret')); //secret은 꼭 숨겨야함
    app.use(session({
        saveUninitialized: false,
        resave: false,
        secret: 'secret', //secret은 꼭 숨겨야함
    });
    app.use(passport.initialize());
    app.use(passport.session());
    

     

    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만 저장
        });
    
        // id로 유저 찾기
        passport.deserializeUser(async (id, done) => {
            try{
                const user = await User.findOne({ where : { id }});
                done(null, user);
    
            }catch(error){
                console.error(error);
                done(error);
            }
        });
    
        local();
    };

     

    dotenv

    secretKey와 같은 노출되면 위험한 값들의 보안을 위한 라이브러리 

     

    $npm i dotenv

     

    root디렉토리에 .env파일 생성 후 .env파일에 숨겨야 할 값들 입력

    COOKIE_SECRET = secretkey12
    DB_PASSWORD = secretkey123

     

    app.js

    
    //dotenv 세팅
    const dotenv = require('dotenv');
    
    dotenv.config();
    
    
    app.use(cookieParser(process.env.COOKIE_SECRET)); //secret은 꼭 숨겨야함
    app.use(session({
        saveUninitialized: false,
        resave: false,
        secret: process.env.COOKIE_SECRET, //secret은 꼭 숨겨야함
    });
    app.use(passport.initialize());
    app.use(passport.session());
    

    tip) json파일에는 dotenv 적용 불가

     

    config.json → js파일로 변경 후 module.exports로 감싸준다.

     

     

    그 후 위 app.js방법과 똑같이 암호화된 값들 입력하면 끝!