diff --git a/package.json b/package.json index 570f3bb..5626a53 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "@next/font": "13.1.6", "axios": "^1.3.3", "formik": "^2.2.9", + "http-status": "^1.6.2", "next": "13.1.6", "react": "18.2.0", "react-dom": "18.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5162406..befaa3d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,6 +15,7 @@ specifiers: eslint-config-next: ^13.1.6 eslint-config-prettier: ^8.6.0 formik: ^2.2.9 + http-status: ^1.6.2 husky: ^8.0.0 lint-staged: ^13.1.2 next: 13.1.6 @@ -33,6 +34,7 @@ dependencies: '@next/font': 13.1.6 axios: 1.3.3 formik: 2.2.9_react@18.2.0 + http-status: 1.6.2 next: 13.1.6_biqbaboplfbrettd7655fr4n2y react: 18.2.0 react-dom: 18.2.0_react@18.2.0 @@ -1867,6 +1869,11 @@ packages: react-is: 16.13.1 dev: false + /http-status/1.6.2: + resolution: {integrity: sha512-oUExvfNckrpTpDazph7kNG8sQi5au3BeTo0idaZFXEhTaJKu7GNJCLHI0rYY2wljm548MSTM+Ljj/c6anqu2zQ==} + engines: {node: '>= 0.4.0'} + dev: false + /human-signals/3.0.1: resolution: {integrity: sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==} engines: {node: '>=12.20.0'} diff --git a/src/api/auth.ts b/src/api/auth.ts index 5c82025..822c9a6 100644 --- a/src/api/auth.ts +++ b/src/api/auth.ts @@ -11,6 +11,14 @@ export interface Token { refreshToken: string } +export interface TokenRefreshPayload { + refreshToken: string +} + export async function login(data: LoginInputDto) { return axios.post('/api/auth/login', data) } + +export async function refreshToken(data: TokenRefreshPayload) { + return axios.post('/api/auth/token', data) +} diff --git a/src/utils/axios.ts b/src/utils/axios.ts index e56ac48..7a2e4dc 100644 --- a/src/utils/axios.ts +++ b/src/utils/axios.ts @@ -1,5 +1,45 @@ -import axios from 'axios' +import axios, { type AxiosError } from 'axios' +import Router from 'next/router' +import status from 'http-status' +import * as api from '@/api' -const instance = axios.create({}) +axios.interceptors.request.use(function (config) { + const accessToken = localStorage.getItem('accessToken') + config.headers.Authorization = `Bearer ${accessToken}` + return config +}) -export default instance +axios.interceptors.response.use( + function (response) { + return response + }, + async function (error: AxiosError) { + // fail to refresh token + if ( + error.config && + error.config.url === '/api/auth/token' && + error.config.method === 'post' + ) { + Router.push('/login') + return Promise.reject(error) + } + switch (error.response?.status) { + case status.UNAUTHORIZED: { + const refreshToken = localStorage.getItem('refreshToken') + if (!refreshToken) { + Router.push('/login') + return Promise.reject(error) + } + const res = await api.auth.refreshToken({ refreshToken }) + localStorage.setItem('accessToken', res.data.accessToken) + localStorage.setItem('refreshToken', res.data.refreshToken) + return error.config && axios.request(error.config) + } + default: { + return Promise.reject(error) + } + } + }, +) + +export default axios diff --git a/tsconfig.json b/tsconfig.json index 580cc9f..29c2f9d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,7 +12,7 @@ "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, - "jsx": "react-jsx", + "jsx": "preserve", "jsxImportSource": "@emotion/react", "incremental": true, "baseUrl": ".",