[node.js] jsonwebtoken library

민수·2023년 3월 10일
0
post-thumbnail

jsonwebtoken이란?

JWT(Json Web Token)을 생성하고 검증할 수 있는 기능을 제공하는 라이브러리이다.

사용법

  • 라이브러리 다운로드
npm install jsonwebtoken
  • 예시 코드를 위한 설정
const jwt = require("jsonwebtoken");

const payload = {
  nickname: "cloudcoke",
  email: "cloudcoke.dev@gmail.com",
};

const salt = "cloudcoke";

JWT 생성하기

jwt.sign(payload, secretOrPrivateKey, [options])

const token = jwt.sign(payload, salt);

console.log(token);
// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuaWNrbmFtZSI6ImNsb3VkY29rZSIsImVtYWlsIjoiY2xvdWRjb2tlLmRldkBnbWFpbC5jb20iLCJpYXQiOjE2Nzg0NTEwMzZ9.S81V0lN6cnqPAMpQFr40Mmn4r0BlrNNF5Y6RUY4z-9Y
  • iat(Issued At) : 토큰 발급 시간
  • iat는 지정하지 않는 한 기본적으로 생성된다. (UNIX Timestamp)
  • signature를 생성할 때 사용하는 알고리즘은 기본적으로 HMAC SHA256(HS256)을 사용한다.

옵션

알고리즘 지정 및 키파일 사용

키파일 생성

const crypto = require("crypto");
const fs = require("fs");

// Generate RSA key pair
const { privateKey, publicKey } = crypto.generateKeyPairSync("rsa", {
  modulusLength: 2048,
  publicKeyEncoding: {
    type: "spki",
    format: "pem",
  },
  privateKeyEncoding: {
    type: "pkcs8",
    format: "pem",
  },
});

fs.writeFileSync("private.pem", privateKey);
fs.writeFileSync("public.pem", publicKey);

JWT 생성

const fs = require("fs");

const privateKey = fs.readFileSync("private.pem");
const token = jwt.sign(payload, privateKey, { algorithm: "RS256" });

console.log(token);
// eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJuaWNrbmFtZSI6ImNsb3VkY29rZSIsImVtYWlsIjoiY2xvdWRjb2tlLmRldkBnbWFpbC5jb20iLCJpYXQiOjE2Nzg0NTYxMjN9.cP6Rs1sjc63QkGltv8AIuaCzLpy2_vdNudxkLXC2C9gcTCFli6yIgEHa_glegmuIbF4kriZEC627dGe5m8qlUovbo34QyWTpljdOnB18wrPgs1khP2iK0YKObYRr00gacTl6zQemaGLx4H6HwY9XlAtqG8oHEskJwhbyaLvm6HniR46FtxmllnmZPXYIsX87IqGoS9KvwOFwnTpjOQCccFeDb4iY5RfmVbvtyC4HlGGbH8rwXe7uuJn95IeoFR6P6XOM05Po5RiWX0VfmarjkiiwafFwU46u1Dzvbj81pZ3CN0n_NZPF9xRstc87wTZ6MpQAqCnQodVCtQ0_KG33TA
  • 개인키를 이용해 토큰 생성을 진행한다.

expiresIn (만료 시간 지정)

const token = jwt.sign({ ...payload, exp: Math.floor(Date().now / 1000) + 60 * 60 }, salt);
const token = jwt.sign(payload, salt, { expiresIn: 60 * 60 });
const token = jwt.sign(payload, salt, { expiresIn: "1h" });
  • expiresIn
    • 기본적으로 만료 시간을 현재 시간을 기준으로 지정한다.
    • 숫자형은 초 수로 인식된다.
    • 문자열로 지정하는 경우 시간 단위를 지정해야 한다.
    • 60 : 60초 후, '2 days' / '2d' : 2일 뒤, '10h' : 10시간 뒤
    • 결과는 UNIX Timestamp로 나온다.

등록된 claims options

  • 소괄호안은 RFC 7519 JWT Registered Claim Names
  • issuer : 토큰 발급자를 의미한다. (iss)
  • subject : 토큰 제목을 의미한다. (sub)
  • audience : 토큰 수신자를 의미한다. (aud)
  • expiresIn : 토큰 만료 시간을 의미한다. (exp)
  • notBefore : 토큰 활성 날짜를 의미한다. [이 날짜 이전의 토큰은 승인되어서는 안됨 ] (nbf)
  • iat : 토큰 발급 시간을 의미한다.
  • jwtid : JWT 토큰 고유 식별자를 의미한다. [issuer가 여러명일 때 구분하기 위해] (jti)
const token = jwt.sign(payload, salt, { subject: "sample" });


// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuaWNrbmFtZSI6ImNsb3VkY29rZSIsImVtYWlsIjoiY2xvdWRjb2tlLmRldkBnbWFpbC5jb20iLCJpYXQiOjE2Nzg0NzE0MTAsInN1YiI6InNhbXBsZSJ9.i-4evt7oSORHlnS08Hf9p3jCVxO8WP3F_U28heIrJX0

JWT 검증하기

  • 예시 코드를 위한 설정
const jwt = require("jsonwebtoken");

const payload = {
  nickname: "cloudcoke",
  email: "cloudcoke.dev@gmail.com",
};

const salt = "cloudcoke";
const token = jwt.sign(payload, salt);

jwt.verify(token, secretOrPublicKey, [options])

const decode = jwt.verify(token, salt);

console.log(decode);
/*
{
  nickname: 'cloudcoke',
  email: 'cloudcoke.dev@gmail.com',
  iat: 1678465847
}
*/
  • 인증에 성공하면 payload값을 복호화해서 반환해 준다.
const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuaWNrbmFtZSI6ImNsb3VkY29rZSIsImVtYWlsIjoiY2xvdWRjb2tlLmRldkBnbWFpbC5jb20iLCJpYXQiOjE2Nzg0NTY3OTUsImV4cCI6MTY3ODQ2MDM5NX0.9LsHQ1427UjYQAEg7K_DMOKayR6VAw52HNdPolhFfmE";

try {
  const decode = jwt.verify(token, salt);
  console.log(decode);
} catch (e) {
  console.log(e.name);
  console.log(e.message);
  console.log(e.expiredAt);
}
/*
TokenExpiredError
jwt expired
2023-03-10T14:59:55.000Z
*/
  • 기본적으로 iat에 지정된 시간이 만료가 되면 jwt expired 오류를 반환해 준다.

옵션

complete

const decode = jwt.verify(token, salt, { complete: true });

console.log(decode);
/*
{
  header: { alg: 'HS256', typ: 'JWT' },
  payload: {
    nickname: 'cloudcoke',
    email: 'cloudcoke.dev@gmail.com',
    iat: 1678469386
  },
  signature: 'dahdXOrj2BEO_hOegr-oMOUz_KKIPQCAdv8fYlDSgas'
}
*/
  • header, payload, signature를 모두 복호화해서 반환해 준다.

알고리즘 지정 및 키파일 사용

const fs = require("fs");

const privateKey = fs.readFileSync("private.pem");
const publicKey = fs.readFileSync("public.pem");

// const token = jwt.sign(payload, salt);
const token = jwt.sign(payload, privateKey, { algorithm: "RS256" });

// 인증을 할 때 공개키를 사용
const decode = jwt.verify(token, publicKey, { algorithms: "RS256" });

console.log(decode);
  • 여러 알고리즘에 대해서 인증을 진행하려면 { algorithms: ["RS256", "RS384"] } 처럼 배열로 작성해주면 된다.
const token = jwt.sign(payload, salt, { subject: "sample" });

try {
  const decode = jwt.verify(token, salt, { subject: "sample" });
  console.log(decode);
} catch (e) {
  console.log(e.name);
  console.log(e.message);
}
// subject 값이 'sample'이라면 
/*
{
  nickname: 'cloudcoke',
  email: 'cloudcoke.dev@gmail.com',
  iat: 1678473285,
  sub: 'sample'
}
*/
// subject 값이 'sample'이 아니라면
/*
JsonWebTokenError
jwt subject invalid. expected: sample
*/

JWT 복호화

jwt.decode(token, [option])

  • 인증 없이 JWT 토큰을 복화할 때 사용한다.
const decoded = jwt.decode(token);

console.log(decoded);
/*
{
  nickname: 'cloudcoke',
  email: 'cloudcoke.dev@gmail.com',
  iat: 1678473708
}
*/

옵션

complete

const decoded = jwt.decode(token, { complete: true });

console.log(decoded);
/*
{
  header: { alg: 'HS256', typ: 'JWT' },
  payload: {
    nickname: 'cloudcoke',
    email: 'cloudcoke.dev@gmail.com',
    iat: 1678474857
  },
  signature: '7ATHWfzG2LglV4eR7InYxfJOXECia0BKbRv2u7Ua92A'
}
*/
  • 복호화된 header, payload, signature를 모두 반환한다.

Error Code

  • TokenExpiredError
    • jwt expired : 토큰의 만료되었을 경우 발생한다.
  • JsonWebTokenError
    • invalid token : 토큰의 header 또는 payload 값을 분석할 수 없을 경우 발생한다. JWT 토큰이 올바른지 비밀 키가 올바른지 확인해 봐야 한다.
    • jwt malformed : 토큰 형식이 올바르지 않을 경우 발생한다. 토큰에 header, payload, signature가 존재하는지와 .으로 구분되어 있는 되어 있는지 확인해 봐야 한다.
    • jwt signature is required : verify() 메서드를 사용 시 토큰에 signature가 존재하지 않거나 잘못되었을 경우 발생한다. 토큰에 signature가 존재하는지 또는 올바른지 확인해 봐야 한다.
    • invalid signature : 토큰의 signature가 올바르지 않을 경우 발생한다.
    • jwt audience invalid. expected: [OPTIONS AUDIENCE] : verify() 메서드 사용시 토큰의 payload에 audience claim 값이 없거나 옵션에 지정한 값과 일치하지 않을 경우 발생한다.
    • jwt issuer invalid. expected: [OPTIONS ISSUER] : verify() 메서드 사용시 토큰의 payload에 iss claim 값이 없거나 옵션에 지정한 값과 일치하지 않을 경우 발생한다.
    • jwt id invalid. expected: [OPTIONS JWT ID] : verify() 메서드 사용시 토큰의 payload에 jti claim 값이 없거나 옵션에 지정한 값과 일치하지 않을 경우 발생한다.
    • jwt subject invalid. expected: [OPTIONS SUBJECT] : verify() 메서드 사용시 토큰의 payload에 sub claim 값이 없거나 옵션에 지정한 값과 일치하지 않을 경우 발생한다.
  • NotBeforeError
    • jwt not active : verify() 메서드 사용시 nbf claim 값이 현재 시간보다 이전인 경우 발생한다.

참고

jsonwebtoken github
JWT 공식 홈페이지 - 소개
rfc7519

0개의 댓글