login 구현 server - token

돌리의 하루·2023년 3월 9일
0
post-thumbnail

로그인을 구현하는 서버를 의사코드와 함께 작성했다.

알고가야할점 👀


1. 유저가 로그인 하면 db에서 유저를 찾는다
2. 유저가 있을 경우 : userinfo.js에서 설정해준다.
3. 유저가 없을 경우 : login.js에서 설정해준다.

📚login.js

유저가 없을 경우엔 바로 return 문으로 "not authorized" 반환해준다.

//login.js
const { USER_DATA } = require("../../db/data");
const { generateToken } = require("../helper/tokenFunctions");

module.exports = async (req, res) => {
  const { userId, password } = req.body.loginInfo;
  const { checkedKeepLogin } = req.body;
 
  const userInfo = {
    ...USER_DATA.find(
      (user) => user.userId === userId && user.password === password
    ),
  };

  //1 user없음
  if (!userInfo.id) return res.status(401).send("Not Authorized"); //얼리리턴. 찾으면 리턴해줌. 다음 내용 로그인하면 보여줄필요가 없으니까

  //2 구조분해할당으로 각각 변수 설정해줌
  const { accessToken, refreshToken } = generateToken(
    userInfo,
    checkedKeepLogin
  );

  //쿠키옵션설정
  const cookiesOption = {
    domain: "localhost",
    path: "/",
    httpOnly: true,
    sameSite: "none",
    secure: true,
  };

  //access토큰을 갖는 쿠키
  res.cookie("access_jwt", accessToken, cookiesOption);

  //refresh 토큰이 있을때
  if (refreshToken) {
    //기간만료를 설정해줌
    cookiesOption.maxAge = 1000 * 60 * 24;

    res.cookie("refresh_jwt", refreshToken, cookiesOption);
  }

  //refreshtoken이 검증된 토큰이고, 다시 accesstoken을 발급해주고난 후, 다시 accessToken인지 검증받게하려고 상태를 위로 올림
  res.status(302).redirect("/userinfo");
  
};

📚userinfo.js

  1. 엑세스 토큰이 있다면 -> 유저 정보를 찾는다
  2. 비밀번호 정보는 민감하니 삭제하고 user정보를 보내준다.


    - 1. 엑세스 토큰이 없으면 리프레시 토큰을 검증한다.
    - 1) 리프레시 토큰을 검증에 실패했다면 로그인 실패로 반환해준다.
    - 2) 리프레시 토큰이 있다면 유저를 찾고


    - 1+ 유저가 없다면 "Not Found"
    - 2+ 유저가 있다면 액세스 토큰을 갱신해준다.


    액세스 토큰과 리프레시 토큰이 없다면 "NOT Authorized" 반환
//userinfo..js
const { USER_DATA } = require("../../db/data");
const { verifyToken, generateToken } = require("../helper/tokenFunctions");

module.exports = async (req, res) => {
  const { access_jwt, refresh_jwt } = req.cookies;
  //req.cookies.access_jwt
  //액세스토큰이 있다면
  if (access_jwt) {
    const accessTokenPayLoad = verifyToken("access", access_jwt);

    //액세스 토큰이 유효하다면
    if (accessTokenPayLoad) {
      //유저정보 찾고
      const userInfo = {
        ...USER_DATA.find((user) => user.id === accessTokenPayLoad.id),
      };
      //비밀번호 삭제후에 user정보 보내준다
      delete userInfo.password;
      return res.send(userInfo);
    }
    //리프레쉬 토큰 있는지 없는지 검사
    //1. 리프레쉬 토큰이 있을때 -> 검증해보고 맞다면 유저를 찾고,
    //유저가 있을때는 엑세스 토큰을 갱신
    //유저가 없다면 유저가 없다고 반환
    if (refresh_jwt) {
      //리프레쉬 토큰 검증
      //밑의 변수는 verifyToken으로 리프레쉬 토큰을 검증해주는 함수를 저장
      const refreshTokenPayLoad = verifyToken("refresh", refresh_jwt);

      //만약 리프레쉬 토큰 검증에 실패했다면 반환값 (단호)
      if (!refreshTokenPayLoad) return res.status(401).send("Not Authorized");

      //유저를 찾고
      const exUser = {
        ...USER_DATA.find((user) => user.id === refreshTokenPayLoad.id),
      };
      //유저가 없다면
      if (!exUser) return res.status(404).send("Not Found");
      //유저가 있다면 액세스 토큰 갱신
      const cookiesOption = {
        domain: "localhost",
        path: "/",
        httpOnly: true,
        sameSite: "none",
        secure: true,
      };
      res.cookie("access_jwt", generateToken(exUser, false), cookiesOption);

      return res.send(exUser);
    }
  }

  return res.status(401).send("Not Authorized");
};

📚logout.js

clearCoockie로 access,refresh토큰을 지워주고, 205상태와 함께 "logout"을 보내준다.

//logout.js
module.exports = (req, res) => {
  const cookiesOption = {
    domain: "localhost",
    path: "/",
    httpOnly: true,
    sameSite: "none",
    secure: true,
  };

  res.clearCookie("access_jwt", cookiesOption);
  res.clearCookie("refresh_jwt, cookiesOption");

  res.status(205).send("logout");
};

위 코드들의 함수들 중 verifyToken, generateToken을 이해하는 것이 중요한데, 코드를 보면서 파악하면 이해하기 쉽다.

//tokenFunction.js
require("dotenv").config();
const { sign, verify } = require("jsonwebtoken");

module.exports = {
  generateToken: (user, checkedKeepLogin) => {
    const payload = {
      id: user.id,
      email: user.email,
    };
    let result = {
      accessToken: sign(payload, process.env.ACCESS_SECRET, {
        //jwt를 반환한다
        expiresIn: "1d", // 1일간 유효한 토큰을 발행합니다.
      }),
    };

    if (checkedKeepLogin) {
      result.refreshToken = sign(payload, process.env.REFRESH_SECRET, {
        expiresIn: "7d", // 일주일간 유효한 토큰을 발행합니다.
      });
    }
    return result;
  },
  verifyToken: (type, token) => {
    let secretKey, decoded;
    switch (type) {
      case "access":
        secretKey = process.env.ACCESS_SECRET;
        break;
      case "refresh":
        secretKey = process.env.REFRESH_SECRET;
        break;
      default:
        return null;
    }

    try {
      decoded = verify(token, secretKey);
    } catch (err) {
      console.log(`JWT Error: ${err.message}`);
      return null;
    }
    return decoded;
  },
};
profile
진화중인 돌리입니다 :>

0개의 댓글