JWT토큰 인증 방식 로그인

2023. 3. 14. 16:13프론트엔드

날짜: 2023년 3월 9일
태그: 교육, 원티드 프리온보딩

❔ 토큰이란

사전적 정의

토큰은 화폐 대신 사용할 수 있는 동전처럼 생긴 주조물을 말한다.

현실세계에서 토큰을 사용하는 경우는 꽤나 있다. 지하철 티켓… 놀이공원 자유이용권… 조건을 확인할 수 있는 경우에 주로 사용한다.

 

 

기술적 정의

그럼 디지털 세상에서 토큰이란? 로그인을 하는방법, 이유와 일치 한다.

 

토큰 기반 인증이란 사용자가 자신의 아이덴티티를 확인하고 고유한 액세스 토큰을 받을 수 있는 프로토콜을 말합니다.
(출처 : https://www.okta.com/kr/identity-101/what-is-token-based-authentication/)

 

로그인에 토큰을 사용하는 이유

 

토큰을 사용하지 않는 경우

  • 유저는 로그인을 통해 신원확인을 했다.
  • 하지만 이 사이트는 별도로 유저의 인증방법이나 상태를 관리하지 않는다. (토큰이나, 세션)
  • 나중에 다른 기능을 요청할 때, 별도로 유저의 신원을 확인해야 하는 불상사가 벌어진다.

 

토큰 기반 인증으로 로그인을 구현

  • 사용자는 한 번 로그인을 하고 유저정보를 확인한 후에 서버에서 토큰을 발급 받음
  • 그 이후의 프론트의 기능 요청에는 고유한 토큰을 함께 전송해서 자신이 인증된 사용자임을 증명함
  • 서버는 토큰을 이용해서 user를 인증하고 올바른 응답을 전송할 수 있음

 

실제 서비스에서의 구조

  • 토큰 발급 및 토큰 저장
  • 토큰 유효성 검증

프론트에서는 발급 받은 토큰을 알맞는 방식으로 저장해서 같이 전송해주는 역할을 해주면 된다. 토큰 발급이나 유효성 검증은 서버에서 해줘야 하는 역할이다.

 

JWT가 무엇인지

JWT (Json Web Token)란 ?

규격에 맞는 토큰을 생성해주는 기술 또는 토큰이다.

 

JWT의 구조

JWT.IO

 

JWT.IO

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

jwt.io

JWT사이트를 가보면 다음과 같은 사진이 있다.

 

일단 크게 Encoded Decoded란이 있다.

  • Encoded : 정보를 암호화 시키는 것
  • Decoded : 암호화된 정보를 부호화하는 것

JWT는 일련의 암호화된 문자열이다. JWT를 만드는 방법은 아래의 데이터를 서버에서 해싱알고리즘으로 부호화 시켜서 발급한다. decoded된 몸체를 살펴보자.

JWT = HEADER.PAYLOAD.SIGNATURE 
  • HEADER : JSON 파일로 암호화 규칙과 토큰 타입을 속성으로 가진다.
  • PAYLOAD : JSON 파일로 데이터를 가진다.
  • VERIFY SIGNATURE : 암호화를 하기 위한 데이터가 들어있다.

 

JWT 유효성 검증 방식

인증을 위한 JWT 발급을 데이터를 HASHING해서 암호화 시키고 사용한다. 이것은 다음과 같은 특징이 있다.

  • Hash는 단방향 암호화 이다. 이것은 다시 복호화 할 수 없음을 의미 한다.
  • 따라서 JWT 유효성검증은 서버에서 user 정보를 다시 해싱해서 JWT를 만들어 이를 대조해서 진행한다.
해시함수(헤더+페이로드+해싱시크릿키) === 토큰의 값

발생할 수 있는 보안 문제

  1. 토큰을 탈취
  2. 시크릿키 노출 : JWT를 마음대로 발급할 수 있기 때문에 위험
  3. 데이터 복호화로 인한 유출 : JWT의 구조에서 페이로드는 디코딩으로 인한 데이터 유출의 위험이 있음.

 

통신을 할때 토큰을 어떻게 보내야 하지?

프론트 관점에서 예제를 살펴보자

 

로그인 후 토큰을 발급 받는 기능

const loginWithToken = async (
  args: LoginRequest
): Promise<LoginResultWithToken> => {
  const result = await fetch(BASE_URL + "/auth/login", {
    headers: {
      "Content-Type": "application/json",
    },
    method: "post",
    body: JSON.stringify(args),
  });

  const { access_token } = await result.json();

  if (!access_token) {
    return {
      result: "fail",
      access_token: null,
    };
  }

  return {
    result: "success",
    access_token,
  };
};
  • “post” 요청으로 id/pw 정보를 전송을 한다.
  • 이 때 서버에서 정보가 맞을시에 access_token을 발급한다.

JWT 토큰을 헤더에 넣고 기능 요청 하기

export const getCurrentUserInfoWithToken = async (
  token: string
): Promise<UserInfo | null> => {
  const result = await fetch(BASE_URL + "/profile", {
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${token}`,
    },
    method: "get",
  });

  const  userInfo = await result();

  return userInfo.json() ? userInfo : null;

};
  • 토큰을 발급 받은 상황에서 get 요청을 통해 본인의 userInfo를 가져 온다.
  • 이 때 header에 Authorization 값에 Bearer 을 함께 넣는다.
  • 토큰이 유효하면 userInfo가 반환된다.

자동로그인 기능 구현해보기

export const saveAccessTokenToLocalStorage = (accessToken: string) => {
  localStorage.setItem('accessToken', accessToken)
}

export const getAccessTokenFromLocalStorage = (): string => {
  return localStorage.getItem('accessToken') || ''
}
export const getCurrentUserInfo = async (): Promise<UserInfo | null> => {
  const token = getAccessTokenFromLocalStorage();

  const result = await fetch(BASE_URL + "/profile", {
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${token}`,
    },
    method: "get",
  });

  const { userInfo } = await result.json();

  return userInfo;
};
  • 로그인 할 때, 발급 받은 토큰을 LocalStorage에 저장한다.
  • 이후에 getCurrentUserInfo 함수에서 LocalStorage에 접근해 저장된 Token을 불러와서 현재 접속하고있는 유저의 정보를 가져온다.
  • 이후 페이지가 렌더링 되기전에 Token이 유효한지 확인하고, 자동로그인을 수행한다.

 

JWT 보관 방식과 보안

  • 로컬 스토리지 or쿠키 에 저장하는데 XSS보안 문제(쿠키 , 로컬 저장)와 CSRF 보안 문제 (쿠키 저장)가 있을 수 있음.
  • XSS (Cross Site String) : 웹사이트에 스크립트를 넣어서 쿠키나 세션을 탈취하는 행위
  • CSRF (Cross site Request Forgery) : 클라이언트의 쿠키를 탈취해서 가짜 요청을 서버에 전송나는 행위

시스템 설계로 해결하기 Refresh token과 Access token

 

Refresh Token의 목적은 Access Token의 유효 기간을 짧고, 자주 재발급 하도록 만들어 보안을 강화하면서도 사용자에게 잦은 로그아웃 경험을 주지 않도록 하는 것이다.

Access Token은 리소스에 접근하기 위해서 사용되는 토큰이라면, Refresh Token은 기존에 클라이언트가 가지고 있던 Access Token이 만료되었을 때 Access Token을 새로 발급받기 위해 사용한다.