본문 바로가기
기억 저장소

JS로 JWT 로그인 구현 (Passport.js 미사용)

by halls99 2022. 10. 2.

🎬 구현한 동작 시나리오

 

🔶: 사용자 시점

 

🔷: 서버 시점

 

 

🔶 쇼핑몰 사이트에서 사용자가 id를 입력 후 로그인 버튼을 클릭

 

🔷 userId (사용자가 입력한 id)를 가지고 access token과 refresh token을 발급하여 JWT를 cookie에 저장

 

🔶 사용자는 로그인을 했으니 자기 계정의 장바구니를 클릭

 

🔷 클라이언트의 cookie를 받아 JWT의 access token이 만료되지는 않았는지, 서버가 발행한 토큰이 맞는지 검증

 

        🔹 access token이 모두 정상이라면, 자연스레 통과

 

        🔹 access token이 유효하지 않은 토큰이라면, cookie의 jwt를 지우고 사용자에게 재로그인 요청

 

        🔹 access token이 만료되었다면, refresh token을 대상으로 만료되지는 않았는지, 서버가 발행한 토큰이 맞는지 검증

 

                🔹 refresh token이 모두 정상이라면, access token을 새로 발급하여 cookie에 저장

 

                🔹 refresh token이 유효하지 않은 토큰이라면, cookie의 jwt를 지우고 사용자에게 재로그인 요청

 

                🔹 refresh token이 만료되었다면, cookie의 jwt를 지우고 사용자에게 재로그인 요청

 

🔶 사용자는 token의 상태에 따라 자연스레 장바구니를 들어갈 수도, 재로그인을 해야 할 수도 있음

 

 

 


코드

 

코드 설명에 앞서 Passport.js 라이브러리를 사용하지 않았고,

 

Express.js 환경에서 jsonwebtoken 모듈을 사용하여 구현하였다.

 

JWT 발행

 

사용자가 처음 로그인을 하거나 재로그인을 해야 할 경우 JWT를 새로 발행한다.

 

const jwt = {
  accessToken : getToken().accessToken(userId),
  refreshToken: getToken().refreshToken(userId),
};
res.cookie("jwt", jwt);

 

userId로 access token, refresh token을 발행하여 cookie에 저장하는 모습이다.

 

 

 

const jwt = require("jsonwebtoken");

export const getToken = () => {
  return {
    accessToken(userId) {
      return jwt.sign(
      	{ 
          userId
        }, 
        process.env.ACCESS_TOKEN_SECRET, 
        {
          expiresIn: "20m",
          issuer: "you",
      	}
      );
    },
    refreshToken(userId) {
      return jwt.sign(
      	{ 
          userId 
        },
        process.env.REFRESH_TOKEN_SECRET, 
        {
          expiresIn: "30 days",
          issuer: "you",
        }
      );
    },
  };
};

 

jwt.sign() 메서드는 npm의 jsonwebtoken 모듈 명세를 참고하여

 

담고 싶은 정보, secret key, 만료 기간, 발행자를 저장하여 암호화 후 생성했다.

 

이 외에도 여러가지 옵션을 줄 수 있다.

 

 

 

JWT 검증

 

사용자가 로그인 한 상태에서 장바구니, 채팅 목록 등 private 한 동작을 하려 할 때 JWT를 검증한다.

 

아래 코드들은 모두 하나의 함수안에 담긴 코드 조각들인데, 뜯어서 차근차근 설명하겠다.

 

let loginFlag = false;

const jwt = req.cookies?.jwt;
if (jwt === undefined) {
  res.send({ loginFlag });
  return;
}

 

loginFlag는 이 함수의 return 값이며,

 

true라면 유효한 사용자이기 때문에 자연스레 넘어가고,

 

false라면 어떠한 이유로 유효하지 않다 판단하여 cookie의 JWT를 지우고 재로그인을 요청한다.

 

보다시피 cookie의 JWT를 추출하여 없다면 false를 return 한다.

 

 

const { accessToken, refreshToken } = jwt;
const accessTokenStateCode = getTokenStateCode(accessToken, TOKEN_TYPE.access);

 

access token을 검증하여 토큰 상태 코드를 저장한다.

 

토큰 상태 코드는 총 3가지로 나타내도록 구현했다.

 

VALID: 만료되지 않았고, 발행자가 유효함

INVALID_ISSUER: 발행자가 유효하지 않음

EXPIRED: 토큰이 만료됨

 

 

 

if (accessTokenStateCode === TOKEN_STATE_CODE.VALID) {
  loginFlag = true;
}

 

access token이 유효하다면 자연스럽게 넘어간다.

 

 

 

if (accessTokenStateCode === TOKEN_STATE_CODE.INVALID_ISSUER) {
  res.clearCookie("jwt");
  loginFlag = false;
}

 

access token의 발행자가 유효하지 않다면, cookie의 jwt를 지우고 재로그인을 요청한다.

 

 

 

if (accessTokenStateCode === TOKEN_STATE_CODE.EXPIRED) {

  // refresh token 상태 코드
  const refreshTokenStateCode = getTokenStateCode(refreshToken, TOKEN_TYPE.refresh);

  // refresh token 유효 - 새 access token 발행 (재로그인 X)
  if (refreshTokenStateCode === TOKEN_STATE_CODE.VALID) {
    const newAccessToken = getNewAccessToken(refreshToken);
    const newJwt = {
      accessToken: newAccessToken,
      refreshToken: refreshToken,
    };
    res.clearCookie("jwt").cookie("jwt", newJwt);
    loginFlag = true;
  } 
  
  // refresh token이 만료되었거나, 발행자가 유효하지 않음 - 재로그인
  else {
    res.clearCookie("jwt");
    loginFlag = false;
  }
}

 

access token이 만료되었다면, refresh token을 검증한다.

 

access token과 마찬가지로 refresh token의 상태 코드를 받아서,

 

VALID라면 새 refresh token으로 새 access token을 발급하여 기존의 cookie를 지우고 새 cookie를 구워 jwt를 저장한다.

 

EXPIRED이거나 INVALID_ISSUER라면, cookie를 지우고 재로그인을 요청한다.

 

 

 

 

 

https://jwt.io/

 

JWT.IO

JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.

jwt.io

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

 

jsonwebtoken

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

www.npmjs.com

댓글