[JavaScript] JWT decoding

yeon·2023년 6월 4일

JavaScript

목록 보기
5/5
post-thumbnail

프로젝트를 진행하면서 로그인한 사용자의 id가 필요한 일이 생겼는데, id를 얻으려고 굳이 api 요청을 늘리는 건 아니라고 생각해서 라이브러리를 사용하지 않고 JWT를 디코딩하여 id를 얻는 방식으로 진행했다. 나중에 또 필요할 일이 있을 것 같아서 이번 기회에 정리해놓고자 한다.

1. 로그인 시 저장한 JWT를 가져온다.

이번 프로젝트의 경우 localStorage에 저장했기 때문에 localStorage에서 가져왔다.
Bearer 부분은 제외해야 하므로 replace() 메소드를 통해 토큰만 구성되도록 했다.

const token = localStorage.getItem('accesstoken')?.replace('Bearer ', '');

2. token에서 payload 부분만 분리한다.

JWT는 '.'으로 구분된 세 부분(header, payload, signature)으로 이루어져 있다. payload에 필요한 정보가 들어있으므로 payload만 분리해서 변수에 할당한다.

const base64Payload = token.split('.')[1];

3. 분리한 payload 부분을 URL-safe한 Base64 문자열로 변환한다.

Base64
데이터를 안전하게 전송하고 저장하기 위해 사용되는 인코딩 방식 중 하나이다.
Base64는 기본적으로 알파벳 대소문자, 숫자, 두 개의 특수문자('+', '/')로 이루어져 있다.

일부 시스템이나 프로토콜에서 URL에서 사용되는 문자 집합과 충돌을 방지하기 위해 URL-safe하게 만들어줘야 한다고 한다.
따라서 JWT에서 분리한 payload를 URL-safe한 Base64 문자열로 변환을 해야 하기 때문에 '-'를 '+'로 대체하고, '_'를 '/'로 대체한다.

const base64 = base64Payload.replace(/-/g, '+').replace(/_/g, '/');

사실 URL-safe에 대한 자세한 원리는 잘 모르겠다.. 일단 프로젝트 진행이 급선무기 때문에 우선 정리해두고 추후에 와서 다시 찾아보기로 하자!

4. base64 문자열을 원래의 JSON 형식 객체로 변환한다.

const decodedJWT = JSON.parse(
      decodeURIComponent(
        window
          .atob(base64)
          .split('')
          .map(function (c) {
            return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
          })
          .join('')
	)
);
  1. JSON.parse()로 JSON 형식 데이터를 다른 컴포넌트에서 객체로 활용할 수 있도록 객체로 파싱한다.

  2. decodeURIComponent()는 URL 인코딩된 문자열을 디코딩해서 원래 문자열로 복원한다. % 기호와 그 뒤에 있는 16진수 값으로 표현된 특수 문자를 원래의 문자로 변환하게 된다.

  3. window.atob(base64)는 Base64 문자열을 디코딩하여 원래의 이진 데이터를 얻는다.

    window.atob()
    Base64를 디코딩하는 JavaScript의 내장 함수

  4. split('') 디코딩된 이진 데이터를 하나씩 문자로 분리해 배열로 만든다. 이렇게 얻은 문자열 배열은 URL 인코딩된 문자로 변환하기 위해 사용된다.

  5. .map(function(c) { ... }) 각 문자에 대해 콜백 함수를 실행해 문자열을 처리한다.

  6. return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)

    (1) URL 인코딩은 % 특수문자와 그 뒤에 따르는 16진수 값으로 표현하는 방식이므로, 맨 앞에 % 문자를 붙여서 URL 인코딩된 문자열 형식으로 만든다.

    (2) 뒤의 내용은 16진수 값으로 표현해야 하니까 .toString(16)으로 16진수 문자열로 변환한다.

    (3) c.charCodeAt(0) 문자의 유니코드 코드 포인트 값을 가져온다. .charCodeAt() 메서드는 문자열 내의 특정 인덱스에 해당하는 문자의 유니코드 값을 반환하는데, 여기서 0을 인덱스로 지정해 첫 번째 문자의 코드 포인트 값을 가져온다. 사실 왜 첫 번째 문자의 코드 포인트 값을 가져오는지는 잘 모르겠다.. 추후에 찾아봐야지

    (4) ('00' + c.charCodeAt(0).toString(16)).slice(-2)는 앞에 0을 추가하여 2자리의 16진수 문자열을 만든다.이렇게 하면 각 코드 포인트의 16진수 문자열이 항상 두 자리로 유지된다고 한다.

    (5) join()으로 배열의 모든 요소를 하나의 문자열로 결합한다. 위 코드에서는 URL 인코딩된 문자열 배열들을 연결해 하나의 문자열로 만든다.

참고 레퍼런스

https://stackoverflow.com/questions/38552003/how-to-decode-jwt-token-in-javascript-without-using-a-library

0개의 댓글