RefreshToken
RefreshToken 이란?
`AccessToken`은 사용자의 로그인 정보를 담고 있는 `JWT토큰 데이터`다
AccessToken 데이터는 일정 시간 동안만 사용할 수 있도록 만료 기한이 정해져 있다
만료 기한이 지나고 사용자가 로그인 정보가 필요한 페이지에 접근하려고 하면 백엔드에서 미리 지정해둔 경로로 redirect 되거나 에러가 뜬다
그렇기 때문에 AccessToken의 만료 기한이 지나면 새로운 AccessToken을 받아와야 한다
이러한 과정에 사용되는 토큰을 RefreshToken이라고 한다
사용자가 올바른 이메일과 비밀번호를 입력해서 로그인하면 백엔드에서 AccessToken과 RefreshToken을 받아온다
이 때 `AccessToken`은 1~2시간 정도의 짧은 만료 기한을 가지고 있고, `RefreshToken`은 2주~1개월 정도의 긴 만료 기한을 가지고 있다
그렇다면 이 RefreshToken을 어디에 담아서 받아올까?
로컬/세션 스토리지의 경우 보안에 취약하기 때문에 토큰을 취급할 때에는 사용하지 않고 쿠키에 RefreshToken을 담아서 받아오게 된다
쿠키라고 해서 매우 안전한 것은 아니지만, 로컬/세션 스토리지와는 다르게 secure, httpOnly 등의 옵션을 설정할 수 있다
왜 번거롭게 AccessToken과 RefreshToken 두 가지 종류의 토큰을 받아오는 걸까?
AccessToken 을 1시간 동안만 사용할 수 있다고 가정했을 때 1시간 1분이 되자마자 이전에 발급 받은 AccessToken은 시간 만료로 인해 사용할 수 없는 토큰이 되어버린다
그럼, 새로운 AccessToken을 발급 받기 위해 다시 로그인을 진행해야 하는데 1시간마다 로그인을 새로 해야 한다면 사용자 입장에서는 엄청난 불편함을 느낄 것이다
이런 불편함을 해결하기 위해서, AccessToken을 발급할 때 RefreshToken이 함께 발급되는 것이다
발급된 AccessToken의 유효 기간이 지나 만료 되는 시점에서 RefreshToken을 통해서 로그인 과정 없이 새로운 AccessToken을 받아올 수 있다
단, RefreshToken 역시 만료 기간이 있기 때문에 RefreshToken 의 만료 기간이 지나게 된다면 이때는 다시 로그인을 진행해 새로운 RefreshToken 을 가져오는 과정이 필요하다
RefreshToken을 이용해 AccessToken을 새로 발급받는 과정
- AccessToken 만료 후 인가 요청
- 해당 오류를 포착해서 인가 에러인지 체크
- RefreshToken으로 AccessToken 재발급 요청
- 발급 받은 AccessToken을 state에 재저장
- 방금 실패했던(error) API를 재요청
RefreshToken 실습
// login-refreshtoken
// 기존 코드의 loginUser API를 loginUserExample API로 변경
// login-refreshtoken-success
// 버튼 클릭시 ApolloClient에서 axios처럼 사용하는 client.query()로 FETCH_USER_LOGGED_IN 데이터 받아오기
const client = useApolloClient()
const onClickButton = async (): Promise<void> => {
const result = await client.query({
query: FETCH_USER_LOGGED_IN,
})
console.log(result)
}
return <button onClick={wrapAsync(onClickButton)}>클릭</button>
// apollo
// RefreshToken 이용해 AccessToken 새로 발급 후 API 재요청하는 로직 작성
const errorLink = onError(async ({ graphQLErrors, operation, forward }) => {
// 1. 에러를 캐치
if (typeof graphQLErrors !== "undefined") {
for (const err of graphQLErrors) {
// 1-2. 해당 에러가 토큰만료 에러인지 체크(UNAUTHENTICATED)
if (err.extensions.code === "UNAUTHENTICATED") {
// 2. refreshToken으로 accessToken을 재발급 받기
const graphQLClient = new GraphQLClient(
"http://backend-practice.codebootamp.co.kr/graphql"
)
const result = await graphQLClient.request(RESTORE_ACCESS_TOKEN)
const newAccessToken = result.restoreAccessToken.accessToken
setAccessToken(newAccessToken)
// 3. 재발급 받은 accessToken으로 방금 실패한 쿼리 재요청하기
operation.setContext({
headers: {
...operation.getContext().headers, // Authorization: Bearer akdjfiejkdsjk => 만료된 토큰이 추가되어 있는 상태
Authorization: `Bearer ${newAccessToken}`, // 3-2. 토큰만 새걸로 바꿔치기
},
})
// 3-3. 방금 수정한 쿼리 재요청하기
forward(operation)
}
}
}
})
'TIL' 카테고리의 다른 글
next.js에서 reactQuill useRef 사용해서 게시글 수정페이지에서 기존값 받아오기 (0) | 2023.08.14 |
---|---|
최근 본 상품 구현 (0) | 2023.08.11 |
이벤트루프 (0) | 2023.08.03 |
apollo-cache-state (0) | 2023.07.22 |
카카오 지도 연동 (0) | 2023.07.22 |