[Auth] mutation을 통해 Authrozation 헤더 추가하기

fejigu·2023년 10월 26일
2

React Native Project

목록 보기
13/21
post-thumbnail

📍 axios 요청시 header에 Authorize

→ 현재 개발중인 서비스는 사용자 인증이 된 사용자만 사용할 수 있기에, 모든 axios 요청시 header에 Authorize: Bearer {토큰 값}을 넣어서 요청해야했다. 매번 요청시 Header에 넣을 수 없기에 mutation을 통해 전역으로 axios의 header에 Authorize: Bearer {토큰 값} 을 넣거나 빼기로 하였다.




📍Authrozation 헤더 추가

1. 필요한 함수 추가하기

→ 나의 경우, utils/auth/orval/setAuthToken.ts 에 작성했지만 파일의 위치는 상관없다.

  • 앱 재시작을 고려하여 setToken을 할때 AsyncStorage 에 데이터를 저장합니다.
  • App.tsx와 setAuthToken.ts에서 axios.defaults.baseURL = BASE_URL;를 통해 baseURL을 지정합니다.
  • axios.interceptors.response.use 통해 토큰 만료시 refreshAccessToken()를 실행합니다.
    • 해당 함수(refreshAccessToken)는 미완성 → API 완성 후 가능 or 재로그인 하여 토큰 재발행
// axiosConfig.ts
import AsyncStorage from "@react-native-async-storage/async-storage";
import axios from "axios";
import { BASE_URL } from "../../../constants/axios";

axios.defaults.baseURL = BASE_URL;
export const setAuthHeader = async (token: string) => {
  axios.defaults.headers.common.Authorization = `Bearer ${token}`;
  return AsyncStorage.setItem("@auth_token", token).catch((e) =>
    console.error("setAuthToken.ts Error while setting or getting token:", e)
  );
};
export const clearAuthHeader = () => (axios.defaults.headers.common.Authorization = undefined);
export const getToken = async () => (await AsyncStorage.getItem("@auth_token")) || null;
export const initToken = async () => {
  const authToken = await AsyncStorage.getItem("@auth_token").catch((e) => console.error("getAuthToken Error:", e));
  authToken ? setAuthHeader(authToken) : clearAuthHeader();
};
// 1. 초기화
// axios.interceptors.request.use((config: InternalAxiosRequestConfig) => config);

// 2. 만약 만료된 토큰일 떄 refreshAccessToklen로 갱신
axios.interceptors.response.use(
  (response) => response,
  async (error) => {
    const originalRequest = error.config;
    // 토큰이 만료되었는지 확인
    if (error.response.status === 401 && !originalRequest._retry) {
      originalRequest._retry = true; // 이 플래그는 같은 요청을 중복으로 재시도하지 않도록 함

      // TODO: refreshToken을 사용하여 accessToken 갱신
      const refreshedToken = "";
      // const refreshedToken = await refreshAccessToken();

      // 토큰 저장 및 헤더 업데이트
      setAuthHeader(refreshedToken);
      // 원래의 요청 재시도
      return axios(originalRequest);
    }
    return Promise.reject(error);
  }
);
// 실제 적용한 코드 
import AsyncStorage from "@react-native-async-storage/async-storage";
import axios from "axios";
import { BASE_URL } from "../../../constants/axios";
import { getNewRefreshTokenData } from "../cognito";
import { CognitoUserSession } from "amazon-cognito-identity-js";

axios.defaults.baseURL = BASE_URL;

//토큰 저장
export const setAuthHeader = async ({
  accessToken: at,
  refreshToken: rt,
  idToken: it,
}: {
  accessToken: string;
  refreshToken: string;
  idToken: string;
}) => {
  axios.defaults.headers.common.Authorization = `Bearer ${it}`;
  await AsyncStorage.setItem("@id_token", it).catch((e) =>
    console.error("setAuthHeader Err @id_token:", e)
  );
  await AsyncStorage.setItem("@access_token", at).catch((e) =>
    console.error("setAuthHeader Err @access_token:", e)
  );
  await AsyncStorage.setItem("@refresh_token", rt).catch((e) =>
    console.error("setAuthHeader Err @refresh_token:", e)
  );

  // 10ms 대기하는 함수를 Promise로 감싸기
  const delay = (ms: number) =>
    new Promise((resolve) => setTimeout(resolve, ms));
  await delay(20); //axios.defaults.headers set 하는데 시간 좀 걸려서 해놓음

  return;
};

//토큰 가져오기
export const getAuthHeader = async () => {
  const it = await AsyncStorage.getItem("@id_token").catch((e) =>
    console.error("getAuthHeader Err @id_token:", e)
  );
  const at = await AsyncStorage.getItem("@access_token").catch((e) =>
    console.error("getAuthHeader Err @access_token:", e)
  );
  const rt = await AsyncStorage.getItem("@refresh_token").catch((e) =>
    console.error("getAuthHeader Err @refresh_token:", e)
  );
  return { accessToken: at, refreshToken: rt, idToken: it };
};

//토큰 삭제
export const clearAuthHeader = async () => {
  await AsyncStorage.clear();
  axios.defaults.headers.common.Authorization = undefined;

  // 10ms 대기하는 함수를 Promise로 감싸기
  const delay = (ms: number) =>
    new Promise((resolve) => setTimeout(resolve, ms));
  await delay(20); //axios.defaults.headers set 하는데 시간 좀 걸려서 해놓음
};

//토큰 axios에 적용하기
export const initToken = async () => {
  const idToken = await AsyncStorage.getItem("@id_token").catch((e) =>
    console.error("getAuthToken Error:", e)
  );
  idToken
    ? (axios.defaults.headers.common.Authorization = `Bearer ${idToken}`)
    : clearAuthHeader();
};
// refresh token이 재기능을 하는지 확인
const checkToken = async (session: CognitoUserSession) => {
  const { accessToken, refreshToken, idToken } = await getAuthHeader();
  const isEqualRefreshToken =
    session.getRefreshToken().getToken() === refreshToken;
  const isEqualIdToken = session.getIdToken().getJwtToken() === idToken;
  const isEqualAccessToken =
    session.getAccessToken().getJwtToken() === accessToken;
  const message = `isEqualRefreshToken: ${isEqualRefreshToken} \n isEqualIdToken: ${isEqualIdToken} isEqualAccessToken: ${isEqualAccessToken}`;
  alert(message);
};

//토큰 만료시 처리
axios.interceptors.response.use(
  (response) => response,
  async (error) => {
    const originalRequest = error.config;
    // axios error가 아닌 경우
    if (!axios.isAxiosError(error)) return Promise.reject(error);

    // 토큰이 만료되었는지 확인
    if (error.response?.status === 401 && !originalRequest?._retry) {
      originalRequest._retry = true; // 이 플래그는 같은 요청을 중복으로 재시도하지 않도록 함

      // TODO: refreshToken을 사용하여 accessToken 갱신
      const token = await AsyncStorage.getItem("@refresh_token").catch((e) =>
        console.error(
          "setAuthToken.ts axios.interceptors Error @refresh_token:",
          e
        )
      );
      // TODO: refreshToken이 없는 경우 로그아웃 처리
      if (!token) {
        alert("Your login has expired. Please log in again.");
        return clearAuthHeader();
      }
      const session: CognitoUserSession | null = await getNewRefreshTokenData(
        token
      ).catch((e) => {
        console.error(e);
        return null;
      });

      // TODO: 회원이 존재하지 않는 경우 로그아웃 처리
      if (session === null) {
        alert("Your login has expired. Please log in again.");
        return clearAuthHeader();
      }

      await checkToken(session);

      // 토큰 저장 및 헤더 업데이트
      setAuthHeader({
        accessToken: session.getAccessToken().getJwtToken(),
        refreshToken: session.getRefreshToken().getToken(),
        idToken: session.getIdToken().getJwtToken(),
      });
      // 원래의 요청 재시도
      return axios(originalRequest);
    }
    // 기타 axios 오류 처리
    return Promise.reject(error);
  }
);




2. 전역 설정

→ App.tsx에 useEffect로 추가하기

  • 기기에 저장된 토큰이 있는지 확인 후 전역으로 initToken을 해줍니다.
import axios from "axios";
import { clearAuthHeader, setAuthHeader } from "./utils/auth/orval/setAuthToken";

// axios의 전역 기본값 설정
axios.defaults.baseURL = BASE_URL;

const App = () => {
  ...

  useEffect(() => {
    const setFont = async () => {
      ...
    };
    setFont();

    initToken();
  }, []);
profile
신규 서비스의 기획부터 개발, 운영까지 전 과정을 경험한 주니어 📱

0개의 댓글