Handling JWTs onto Login

Daye Kang·2020년 5월 17일
0

1. 'JWT'란?

: 'JWT'란'인증(authorization)'을 위해 서버에서 발행한 토큰이다. 이 토큰은 JSON 객체로 사용자에 관한 특정한 정보를 포함하고 있다. 이 토큰은 클라이언트 측에서 API를 보낼 때 사용되는 것으로 해당 토큰에 담겨있는 정보와 '인증'이 필요한 유저를 확인하게 된다.

'JWT'의 구조는?

: 'JWT'는 다음과 같은 모습이다.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.XbPfbIHMI6arZ3Y922BhjWgQzWXcXNrz0ogtVhfEd2o

이 코드를 해독하면 'JSON' 객체를 얻게 되는데, 이는 'header', 'payload', 'signature' 이 세 부분으로 구분된다.

2. 로그인

'JWT'토큰을 받는 간단한 로그인 과정을 살펴보자.

클라이언트 측에서 'username/password'를 인증을 위한 엔드포인트로 요청을 서버측으로 보내고 그 응답으로 JWT 토큰을 보낸다. 후에 클라이언트 측에서 메모리에 토큰을 저장한다.

먼저 로그인 요청을 보내는 코드를 살펴보자.

//로그인 버튼에 `handleSubmit`이라는 핸들러를 넣어 요청을 보냄. 
async function handleSubmit () {
  //...
  // Make the login API call
  const response = await fetch(`/auth/login`, {
    method: 'POST',
    body: JSON.stringify({ username, password })
  })
  //...
  // Extract the JWT from the response
  const { jwt_token } = await response.json()
  //...
  // Do something the token in the login method
  await login({ jwt_token })
}

로그인 API는 응답으로 토큰을 보내고 클라이언트 측에서는 이 토큰을 '/utils/auth'로 login 함수에 넣어준다.

import { login } from '../utils/auth'
await login({ jwt_token })

토큰을 얻었다면, 어디에 저장을 해야할까?

JWT 토큰을 어딘가에 저장해야 할 때, localStorage(로컬스토리지)가 떠오를지도 모르겠다. 하지만 좋은 생각은 아니다. 이 방법은 XSS 공격에 취약하기 때문이다.

'cookie'에 저장하는 건?

안타깝게도 이 방법 또한 XSS 공격에 취약하다. 클라이언트가 아닌 서버에서 만들어진 HttpOnly 쿠키가 대안으로 떠오를지도 모른다. 하지만 이것은 CSRF 공격에 취약하다. HttpOnly와 까다로운 CORS 규칙은 CSRF 공격에서 자유로울 수 없으며 쿠키를 사용하는 것은 적합한 CSRF 완화 전략이 필요하다.

그럼 어디에 저장해야하는 거죠?

일단 메모리에 저장해보자.

let inMemoryToken;

function login ({ jwt_token, jwt_token_expiry }, noRedirect) {
  inMemoryToken = {
    token: jwt_token,
    expiry: jwt_token_expiry
  };
  if (!noRedirect) {
    Router.push('/app')
  }
}

보이는 것처럼 '메모리'에 토큰을 저장했다. 물론 유저가 리디렉팅을 하면 토큰이 날아갈 것이다. 이는 나중에 다루도록 하겠다.

이제 토큰을 갖고 무엇을 할 수 있을까?

  • 모든 api 요청을 날릴 때 header에 담아서 보낼 수 있음.
  • 'JWT'의 존재 유무를 보고 유저의 로그인 여부를 확인할 수 있음.
  • 추가적으로 payload 부분에 있는 데이터를 가져오기 위해 JWT를 복호화하면 됨.

어떻게 유저가 로그인했는지를 알 수 있을까?

const jwt_token = inMemoryToken;
if (!jwt_token) { // 토큰이 없으면
  Router.push('/login') // 로그인페이지로 라우팅
}
return jwt_token // 토큰이 있으면 리턴

토큰 변수가 설정되어 있는지, 그렇지 않다면 다시 로그인 페이지로 리디렉팅되게 함으로써 확인할 수 있다.

profile
뭐든 하자

0개의 댓글