REFRESH_TOKEN
은 사용자가 인증을 받을 때 부여받는 token 중 하나로, ACCESS_TOKEN
이 만료될 경우 새로운 ACCESS_TOKEN
을 받아오기 위해서 사용한다.
ACESS_TOKEN
이 만료되었는가를 확인하는 방법은,
ACESS_TOKEN
의 만료기한을 storage에 같이 저장해두고 판단등이 존재한다.
REFRESH_TOKEN
을 이용해 ACCESS_TOKEN
을 받아올 때에는 token을 받아왔던 TOKEN_ENDPOINT
로
REFRESH_TOKEN
을 담아서요청을 보내주면 새로운 ACCESS_TOKEN
을 받을 수 있다.
token 👇
const {refresh_token} = req.body;
// basic 인증으로 보내기
const basicHeader = Buffer.from(
`${OAUTH_CLIENT_ID}:${OAUTH_CLIENT_SECRET}`
).toString("base64")
const headers = {
"Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
Authorization: `Basic ${basicHeader}`
};
const resp = await axios.post(
OAUTH_TOKEN_ENDPOINT,
qs.stringify({
grant_type: "refresh_token",
refresh_token,
}),
{
headers
}
);
res.status(200).json(resp.data);
Token 갱신을 위해서는 위에서 말했듯이 REFRESH_TOKEN
을 인증 요청에 붙여서 보내야한다.
매번 ACCESS_TOKEN
의 만료를 판단하고 만료됐을 경우 REFRESH_TOKEN
을 통한 재요청을 하기에는 번거로우니 작업의 자동화를 하면 편할 것이다.
이런 token을 항상 자동으로 붙여주는 방법은,
으로 해볼 수 있다.
token refresh를 진행해보기 위해서 axios interceptor
를 사용해본다.
axios interceptor
를 사용하면
에 가로채서 실행을 시킬 수 있다.
요청을 보내기 전에 항상 ACCESS_TOKEN
을 붙여주거나 오류가 발생시에 REFRESH_TOKEN
을 이용한 ACCESS_TOKEN
재요청을 시켜줄 수 있다는 이야기.
다만 모든 요청에서 동일한 작업을 필요로 하는 것은 아니기 때문에 api 호출을 변수값으로 설정해두고 window를 사용해 값을 저장한 다음, 브라우저에서 일어나는 일들은 window의 값을 참조하게하는 방식으로 진행했다.
따라서 특정 주소로 보내지는 api 요청에서만 interceptor가 실행이 된다.
if (typeof window !== "undefined"){
//interceptor 실행 내용
}
axios.interceptors.request.use
를 사용하면 요청이 보내지기 전에 요청을 가로채서 원하는 작업을 하도록 설정할 수 있다.axios.interceptors.request.use(
(cfg) => {
// 요청을 보내기 전에 수행할 일
return cfg;
}, (error) => {
// 오류 요청을 보내기 전에 수행할 일
return Promise.reject(error);
});
interceptors.js 👇
// axios로 요청을 보내기 전 intercept
axios.interceptors.request.use((cfg) => {
// 특정 api endpoint일 때만 token을 붙이도록 설정
if (cfg.url.startsWith(window.API_ENDPOINT)) {
// request를 보낼 때 access token을 자동으로 붙이도록 함
cfg.headers.authorization = `Bearer ${localStorage.getItem("access_token")}`;
}
return cfg;
});
axios.interceptors.response.use
를 사용하면 요청 완료 후 응답을 처리하기 전에 가로채서 원하는 작업을 수행하도록 할 수 있다.axios.interceptors.response.use(
(response) => {
// 응답 데이터 처리
return response;
}, (error) => {
// 오류 응답 처리
return Promise.reject(error);
});
interceptors.js 👇
// axios로부터 response를 받아 처리하기 전에 intercept
axios.interceptors.response.use(
(response) => {
return response;
}, (error) => {
// res에서 error가 발생했을 경우 catch로 넘어가기 전에 처리하는 부분
let errResponseStatus = null;
const originalRequest = error.config;
try {
errResponseStatus = error.response.status;
} catch (e){
}
// access token이 만료되어 발생하는 에러인 경우
if ((error.message === "Network Error" || errResponseStatus === 401) && !originalRequest.retry) {
originalRequest.retry = true;
const preRefreshToken = localStorage.getItem("refresh_token");
if (preRefreshToken) {
// refresh token을 이용하여 access token 재발행 받기
return axios.post(
"/api/oauth/token",
{
grant_type: "refresh_token",
refresh_token: preRefreshToken
}).then((res) => {
const {access_token, refresh_token} = res.data;
// 새로 받은 token들의 정보 저장
localStorage.setItem("access_token", access_token);
localStorage.setItem("refresh_token", refresh_token);
originalRequest.headers.authorization = `Bearer ${access_token}`;
return axios(originalRequest);
}).catch(() => {
// access token을 받아오지 못하는 오류 발생시 logout 처리
localStorage.removeItem("access_token");
localStorage.removeItem("refresh_token");
localStorage.removeItem("profile");
window.location.href = "/";
return false;
});
}
// 오류 발생 시 오류 내용 출력 후 요청 거절
return Promise.reject(error);
}
// 오류 발생 시 오류 내용 출력 후 요청 거절
return Promise.reject(error);
});