모두의 이력서

[모두의 이력서_6일차] Auth 기능 및 로그아웃 기능

개발조각 2023. 3. 30. 20:15
728x90
반응형

처음으로 혼자서 프론트부터 백까지 하는 프로젝트이다 보니 정확하지 않을 수 있습니다!

 

Auth 기능 만들기


Auth(인증)를 만드는 이유

  1. 페이지 이동 때마다 로그인되있는지 안되어 있는지, 관리자 유저인지 등을 체크하기 위해
  2. 글을 쓸때나 지울 때 같은 경우 권한이 있는지도 체크하기 위해

현재 서버는 DB에 토큰을 넣어주었고 클라이언트는 Cookie에 토큰을 넣어주었습니다.

이 서버, 클라이언트에 있는 토큰이 같은 토큰인지 체크해 줘야 됩니다.

 

// jsonwebtoken을 사용해서 토큰 생성
const token = jwt.sign(user._id.toHexString(), "secretToken");

클라이언트에 쿠키에 담겨있는 토큰을 서버에 전달하면 서버쪽 토큰이 인코드 하면 User ID가 나옵니다.

 

jwt로 user._id + 'secretToken'조합으로 토큰을 만들면 token의 디코드를 통해 user._id를 추출해서

해당 아이디를 가진 유저의 DB에 같은 토큰이 존재하는지 확인하면 Auth(Authentication_인증) 기능을 구현할 수 있습니다.

(user._id + 'secretToken' = token에서  token, 'secretToken'을 가지고 user._id를 찾을 수 있습니다.)

 

 

1. Cookie에서 저장된 Token을 Server에서 가져와서 복호화(디코드)를 한다.

유저정보를 가져올 라우터 만들기

// server.ts
app.get("/api/user/auth", auth, (req, res)=>{

});

 

이번에 라우터를 만들때 미들웨어를 만들어야 됩니다.

auth: 미들웨어
미들웨어: 엔드포인트로 reqest를 받고 난 다음에 callback함수 하기 전에 중간에 무언가를 하는 것을 의미

 

폴더 만들기

back
|- middleware
	|- auth.ts
// auth.ts
export const auth = (req, res, next) => {
    // 인증 처리 하는 곳
    // 1.클라이언트 쿠키에서 토큰을 가져온다.
    // 2.토큰을 복호화 한 후 유저를 찾는다.
    // 3.유저가 없으면 인증 No!
    // 4.유저가 있으면 인증 OKay!
};

 

https://www.npmjs.com/package/jsonwebtoken

 

jsonwebtoken

JSON Web Token implementation (symmetric and asymmetric). Latest version: 9.0.0, last published: 3 months ago. Start using jsonwebtoken in your project by running `npm i jsonwebtoken`. There are 23121 other projects in the npm registry using jsonwebtoken.

www.npmjs.com

// verify a token symmetric
jwt.verify(token, 'shhhhh', function(err, decoded) {
  console.log(decoded.foo) // bar
});

이 부분을 참고해서 user.ts에 작성하겠습니다.

 

// User.ts
userSchema.statics.findByToken = function (token, cb) {
  const user = this;

  // 토큰을 decode 한다.
  // decode를 해서 user._id를 찾아야 된다.
  jwt.verify(token, "secretToken", function (err: any, decoded: any) {
    // 유저 아이디를 이용해서 유저를 찾은 다음에
    // 클라이언트에서 가져온 token과 DB에 보관된 토큰이 일치하는지 확인
    user.findOne(
      { _id: decoded, token: token },
      function (err: "empty", user: any) {
        if (err) return cb(err);
        cb(null, user);
      }
    );
  });
};

 

2. 복호화를 하면서 나온 User ID를 이용해서 DB User Collection에서 유저를 찾은 후 쿠키에서 받아온 token이 유저도 갖고 있는지 확인

쿠키가 일치 X -> Authentication False !!
쿠키가 일치 O -> Authentication True!! 그리고 그 해당하는 유저의 정보들을 선별

// auth.ts
import { User } from "./../models/User";

export const auth = (req: any, res: any, next: any) => {
  // 인증 처리 하는 곳
  // 1.클라이언트 쿠키에서 토큰을 가져온다.
  let token = req.cookies.x_auth;
  
  // 2.토큰을 복호화 한 후 유저를 찾는다.
  User.findByToken(token, (err: any, user: any) => {
    if (err) throw err;

    // 3.유저가 없으면 인증 No!
    if (!user) return res.json({ isAuth: false, error: true }); // 클라이언트에게 {isAuth: false, error: true} 전달

    // 4.유저가 있으면 인증 OKay!
    req.token = token;
    req.user = user;
    next(); // server.ts auth 미들웨어가 갈 수 있게 next를 안쓰면 미들웨어에 갇쳐버린다.
  });
};
// server.ts
app.get("/api/user/auth", auth, (req, res) => {
  // 여기까지 미들웨어를 통과해 왔다는 얘기는 Authentication이 True라는 말
  res.status(200).json({
    _id: req.user._id,
    isAdmin: req.user.role === 0 ? false : true,
    isAuth: true,
    email: req.user.email,
    name: req.user.name,
    lastname: req.user.lastname,
    role: req.user.role,
    image: req.user.image,
  });
});

 

 

 

마지막으로 User.ts에서 findOne이 콜백지원을 안 하기 때문에 고쳐줘야 됩니다.

userSchema.statics.findByToken = function (token, cb) {
  const user = this;

  jwt.verify(token, "secretToken", function (err: any, decoded: any) {
    // 유저 아이디를 이용해서 유저를 찾은 다음에
    // 클라이언트에서 가져온 token과 DB에 보관된 토큰이 일치하는지 확인

    // 수정 후
    user
      .findOne({ _id: decoded, token: token })
      .then((user: any) => {
        return cb(null, user);
      })
      .catch((err: "empty") => {
        return cb(err);
      });

    // 수정 전
    // user.findOne(
    //   { _id: decoded, token: token },
    //   function (err: "empty", user: any) {
    //     if (err) return cb(err);
    //     cb(null, user);
    //   }
    // );
  });
};

 

post man으로 확인

 

 

로그아웃 기능


1. 로그아웃 Route 만들기

// server.ts
app.get('/api/users/logout', auth, (req, res)=>{
	// 로그아웃 하려는 유저를 데이터베이스에서 찾아서 데이터를 업데이트
})

 

2. 로그아웃 하려는 유저를 DB에서 찾아서 그 유저의 토큰을 지원 준다.

server.ts
app.get("/api/users/logout", auth, (req: any, res) => {
  // 로그아웃 하려는 유저를 데이터베이스에서 찾아서 데이터를 업데이트
  User.findOneAndUpdate(
    { _id: req.user._id },
    { token: "" },
    (err: "empty", user: any) => {
      if (err) return res.json({ success: false, err });
      return res.status(200).send({
        success: true,
      });
    }
  );
});

 

이번에도 findOneAndUpdate가 콜백지원을 안 함으로 고쳐줘야 됩니다.

app.get("/api/users/logout", auth, (req: any, res) => {
  // 로그아웃 하려는 유저를 데이터베이스에서 찾아서 데이터를 업데이트
  // 수정후
  User.findOneAndUpdate({ _id: req.user._id }, { token: "" })
    .then(() => {
      return res.status(200).send({
        success: true,
      });
    })
    .catch((err: "empty") => {
      return res.json({ success: false, err });
    });

  // 수정전
  // User.findOneAndUpdate(
  //   { _id: req.user._id },
  //   { token: "" },
  //   (err: "empty", user: any) => {
  //     if (err) return res.json({ success: false, err });
  //     return res.status(200).send({
  //       success: true,
  //     });
  //   }
  // );
});

post man으로 확인

 

몽고디비 확인

token: ""이 되었습니다-!

 

참고자료


https://www.youtube.com/watch?v=OGVsnbEbSLM 

 

https://www.youtube.com/watch?v=zye0VrVUfuI&t=1s 

 

728x90
반응형