# passport (일반 로그인)

1. 프론트에서 login을 할 때 email, password를 같이 보내준다.
2. routes/auth.js

`passport.authenticate('local',` 이 부분까지 실행됨.

```jsx
router.post('/login', (req, res, next) => {
  // 1. 먼저 passport가 localStrategy를 찾는다
  passport.authenticate('local', (authError, user, info) => {
    if (authError) {
      console.error(authError);
      return next(authError);
    }

    if (!user) {
      return res.redirect(`/?loginError=${info.message}`);
    }

    return req.login(user, (loginError) => {
      if (loginError) {
        console.error(loginError);
        return next(loginError);
      }

      return res.redirect('/');
    });
  })(req, res, next);
});
```

1. passport/index.js

```jsx
const passport = require('passport');
const local = require('./localStrategy');
const kakao = require('./kakaoStrategy');
const User = require('../models/user');

module.exports = () => {
  passport.serializeUser((user, done) => {
    done(null, user.id);
  });

  passport.deserializeUser(async (id, done) => {
    try {
      User.findOne({ where: { id } });
    } catch (err) {
      done(err);
    }
  });

  local(); // 이 부분이 실행된다.
  kakao();
};
```

1. passport/localStrategy.js

```jsx
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const bcrypt = require('bcrypt');

const User = require('../models/user');

module.exports = () => {
  passport.use(
    new LocalStrategy(
      {
        usernameField: 'email', // req.body.email
        passwordField: 'password', // req.body.password
      },
      // done(error, user, info);
      async (email, password, done) => {
        try {
          const exUser = await User.findOne({ where: { email } });
          if (exUser) {
            const result = await bcrypt.compare(password, exUser.password);
            if (result) {
              done(null, exUser);
            } else {
              done(null, false, { message: '비밀번호가 일치하지 않습니다.' });
            }
          } else {
            done(null, false, { message: '가입되지 않은 회원입니다.' });
          }
        } catch (error) {
          console.error(error);
          done(error);
        }
      }
    )
  );
};

/**
 * done은 Passport.js에서 사용하는 콜백 함수.
 * Passport는 인증 작업을 수행할 때 사용자 정의 인증 로직을 수행하는 데 사용되는 전략(Strategies)을 구현할 수 있다.
 */
```

* **`error`**: 인증 과정에서 발생한 오류가 있는 경우 여기에 전달합니다. 정상적으로 처리되면 \*\*`null`\*\*을 전달합니다.
* **`user`**: 인증에 성공한 사용자 객체를 전달합니다. 인증에 실패한 경우 \*\*`false`\*\*를 전달합니다.
* **`info`**: 인증에 대한 추가 정보를 제공하는 객체입니다. 예를 들어, 에러 메시지나 경고 등을 전달할 수 있습니다.

1. auth.js

* localStrategy의 done 함수를 호출하면 다시 auth.js로 돌아와서 `**(authError, user, info)**`이 부분이 실행된다.
* **`req.login(user)`을 하는 순간 passport/index.js로 간다.**

```jsx
router.post('/login', (req, res, next) => {
  // 1. 먼저 passport가 localStrategy를 찾는다
  passport.authenticate('local', (authError, user, info) => {
    if (authError) {
      console.error(authError);
      return next(authError);
    }
    if (!user) {
      return res.redirect(`/?loginError=${info.message}`);
    }
    return req.login(user, (loginError) => {
      if (loginError) {
        console.error(loginError);
        return next(loginError);
      }
      return res.redirect('/');
    });
  })(req, res, next);
});
```

1. passport/index.js

* `**serializeUser`\*\* 가 실행됨
* **`req.login(user)`** 의 user가 serializeUser의 user로 들어가서 user.id만 뽑아서 done을 한다.

```jsx
const passport = require('passport');
const local = require('./localStrategy');
const kakao = require('./kakaoStrategy');
const User = require('../models/user');

module.exports = () => {
  passport.serializeUser((user, done) => {
    done(null, user.id); // 세션에 user의 id만 저장
  });

  passport.deserializeUser(async (id, done) => {
    try {
      User.findOne({ where: { id } });
    } catch (err) {
      done(err);
    }
  });

  local();
  kakao();
};
```

* **serializeUser:** 사용자가 로그인에 성공하면, 사용자 객체를 세션에 저장하기 위해 **`serializeUser`** 메서드가 호출됩니다. 이 메서드는 사용자 객체를 인수로 받아 세션에 저장할 데이터(보통 사용자 ID)를 선택한 다음, **`done`** 콜백 함수를 호출하여 결과를 반환합니다. 이렇게 하면 선택한 데이터가 세션에 저장되고, 세션 ID가 클라이언트에 쿠키로 전송됨.
* **deserializeUser**: 클라이언트로부터 요청이 들어오면, Passport는 세션 ID를 사용하여 세션 데이터를 조회하고, 이를 바탕으로 사용자 객체를 복원합니다. 이때 **`deserializeUser`** 메서드가 호출되며, 세션에 저장된 데이터(사용자 ID)를 인수로 받습니다. 이 메서드는 사용자 ID를 사용하여 데이터베이스에서 사용자 객체를 조회한 다음, **`done`** 콜백 함수를 호출하여 결과를 반환합니다. 이렇게 하면 사용자 객체가 복원되고, 이후 미들웨어에서 \*\*`req.user`\*\*를 통해 사용할 수 있습니다.

1. routes/auth.js

* serialize가 done 되는 순간 `(loginError) => {}` 이 실행된다.
* 에러가 있으면 error를 표시하고 없으면 홈으로 redirect

```jsx
router.post('/login', (req, res, next) => {
  // 1. 먼저 passport가 localStrategy를 찾는다
  passport.authenticate('local', (authError, user, info) => {
    if (authError) {
      console.error(authError);
      return next(authError);
    }
    if (!user) {
      return res.redirect(`/?loginError=${info.message}`);
    }
    return req.login(user, (loginError) => {
      if (loginError) {
        console.error(loginError);
        return next(loginError);
      }
      return res.redirect('/');
    });
  })(req, res, next);
});
```

#### 정리

1. routes/auth.js - `router.post('/login’`의 `passport.authenticate('local’` 실행
2. passport/localStrategy.js - `done` 실행
3. routes/auth.js - `(authError, user, info) => {` 실행
   * 로그인 성공한 경우에는 `return req.login(user,` 의 user인자가
4. passport/index.js - `serializeUser` 실행, `user` 로 들어가서 `세션쿠키-id` 형태로 메모리에 저장 후 `done`
5. routes/index.js - `(loginError) => {` 실행,
   * 최종적으로 error가 있는지 확인 후 `redirect(’/’)` (redirect 전에 세션쿠키를 브라우저로 보내준다.)

#### 로그인 성공 후

<figure><img src="/files/KSFYYromwdlg8BdQxEOa" alt=""><figcaption></figcaption></figure>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://adam-37.gitbook.io/joomadeung/node.js/node.js-express-mysql/passport.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
