티스토리 뷰

Projects

[Namoldak] TroubleShooting_RefreshToken

soobin Choi 2023. 2. 13. 13:43

✔️ HTTP 헤더

결론 : http header는 대소문자를 구분하지 않음

HTTP 헤더는 클라이언트와 서버가 요청 또는 응답으로 부가적인 정보를 전송할 수 있도록 해줍니다. HTTP 헤더는 대소문자를 구분하지 않는 이름과 콜론 ':' 다음에 오는 값(줄 바꿈 없이)으로 이루어져있습니다.
https://developer.mozilla.org/ko/docs/Web/HTTP/Headers

문제 : token 재발급 api 호출 시 refreshtoken 값을 담아줘야 했으나 request header에 RefreshToken이라는 항목 자체가 없었음

원인 : header에서 refreshtoken 소문자로 key name을 줘야 했음

✔️ access token 만료 시 refreshtoken 을 이용하여 재발급 받는 과정

1. 로그인 시 Cookies에 refreshtoken을 저장함

2. access token이 만료되면 토큰 재발급 api를 호출함

 

3. request header에 refresh token 값을 넣어서 보내줌

 

4. 서버에서 response header에 새로 발급한 access token 값을 보내줌

 

5. 새로운 access token 값을 cookie 에 넣고 로그인 유지

 

*우리는 access token을 30분, refresh token을 7일로 만료시간을 지정했다.

 

✔️ 코드 (폴더 > 파일)

1. src > api > core > axios.js

axios instance와 api 통신 시 사용되는 request header 설정

// 외부 모듈
import axios from 'axios';

// 내부 모듈
import { getAccessToken } from '../../utils/cookies';

// 인스턴스 생성
export const instance = axios.create({
  baseURL: process.env.REACT_APP_API_URL,
  withCredentials: true,
});

// 요청 타임아웃
instance.defaults.timeout = 2500;

// 인스턴스 request header Authorization 설정
instance.interceptors.request.use((config) => {
  if (config.headers === undefined) return;
  const token = getAccessToken();
  if (token) {
    config.headers.AccessToken = `${token}`;
  }
  return config;
});

 

2. src > utils > cookies.jsx

전체 코드 중 access token, refresh token 관련 부분

import { Cookies } from 'react-cookie';
import axios from 'axios';

const REFRESH_TOKEN = 'RefreshToken';
const ACCESS_TOKEN = 'AccessToken';
const cookies = new Cookies();

export const setRefreshToken = (refreshToken) => {
  const today = new Date();
  const expireDate = today.setDate(today.getDate() + 7);

  setTimeout(() => {
    console.log('refreshToken', refreshToken);
    axios
      .get(`${process.env.REACT_APP_API_URL}/auth/issue/token`, {
        headers: {
          refreshtoken: refreshToken,
        },
      })
      .then((response) => {
        setAccessToken(response.headers.accesstoken);
      });
  }, 25 * 60 * 1000);

  cookies.set(REFRESH_TOKEN, refreshToken, {
    sameSite: 'strict',
    path: '/',
    expires: new Date(expireDate),
  });
};

export const setAccessToken = (token) => {
  const expireDate = new Date();
  expireDate.setMinutes(expireDate.getMinutes() + 30);

  cookies.set(ACCESS_TOKEN, token, {
    path: '/',
    expires: expireDate,
  });
};

export const getRefreshToken = () => {
  return cookies.get(REFRESH_TOKEN);
};

export const getAccessToken = () => {
  return cookies.get(ACCESS_TOKEN);
};

export const removeCookie = () => {
  cookies.remove(REFRESH_TOKEN);
  cookies.remove(ACCESS_TOKEN);
};

 

3. src > api > authAsync.jsx

전체 코드 중 login api를 호출하는 부분

// 내부 모듈
import { instance } from './core/axios';

const Login = async (data) => {
  try {
    const response = await instance.post('/auth/login', data);
    return response;
  } catch (error) {
    useToast(`${error.response.data.statusMsg}`, 'error');
  }
  return null;
};

const authAPI = {
  Login,
};

export default authAPI;

 

4. src > components > Login > Login.jsx

login api 호출하고 받은 응답에서 access token과 refresh token을 넣어주는 부분

  // 로그인 api
  async function onClickLogin(data) {
    await authAPI.Login(data).then((response) => {
      setRefreshToken(response.headers.refreshtoken);
      setAccessToken(response.headers.accesstoken);
      setNicknameCookie(response.data.nickname);
      useToast('로그인 되었습니다.', 'success');
      navigate('/');
    });
  }

 

*전체 코드는 깃허브 참고해주세요. 코드의 잘못된 점을 발견하셨다면 답글 부탁드립니다!