Test: 리프레시 토큰 쿠키
This commit is contained in:
parent
81187fb0f2
commit
8efc4c1494
@ -1,20 +1,8 @@
|
|||||||
import api from '@/api/axiosConfig';
|
import api from '@/api/axiosConfig';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
|
|
||||||
export const reissueTokenApi = async () => {
|
export const reissueTokenApi = () => {
|
||||||
try {
|
return api.post('/api/auth/reissue', null, { withCredentials: true }).then((response) => response.data);
|
||||||
const response = await api.post('/api/auth/reissue', null, {
|
|
||||||
withCredentials: true,
|
|
||||||
});
|
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof AxiosError) {
|
|
||||||
console.error('토큰 재발급 실패:', error.response?.data?.message || '알 수 없는 오류');
|
|
||||||
} else {
|
|
||||||
console.error('알 수 없는 오류가 발생했습니다.');
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fetchProfileApi = async () => {
|
export const fetchProfileApi = async () => {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import axios from 'axios';
|
import axios, { AxiosError, AxiosResponse, InternalAxiosRequestConfig } from 'axios';
|
||||||
import useAuthStore from '@/stores/useAuthStore';
|
import useAuthStore from '@/stores/useAuthStore';
|
||||||
|
|
||||||
const baseURL = 'https://j11s002.p.ssafy.io';
|
const baseURL = 'https://j11s002.p.ssafy.io';
|
||||||
@ -8,56 +8,83 @@ const api = axios.create({
|
|||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
api.interceptors.request.use((config) => {
|
let isTokenRefreshing = false;
|
||||||
|
|
||||||
|
type FailedRequest = {
|
||||||
|
resolve: (value?: string | undefined) => void;
|
||||||
|
reject: (reason?: unknown) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
let failedQueue: FailedRequest[] = [];
|
||||||
|
|
||||||
|
const processQueue = (error: Error | null, token: string | undefined = undefined): void => {
|
||||||
|
failedQueue.forEach((prom) => {
|
||||||
|
if (error) {
|
||||||
|
prom.reject(error);
|
||||||
|
} else {
|
||||||
|
prom.resolve(token);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
failedQueue = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
api.interceptors.request.use((config: InternalAxiosRequestConfig) => {
|
||||||
const token = localStorage.getItem('accessToken');
|
const token = localStorage.getItem('accessToken');
|
||||||
if (token) {
|
if (token && config.headers) {
|
||||||
config.headers.Authorization = `Bearer ${token}`;
|
config.headers.Authorization = `Bearer ${token}`;
|
||||||
}
|
}
|
||||||
return config;
|
return config;
|
||||||
});
|
});
|
||||||
|
|
||||||
api.interceptors.response.use(
|
api.interceptors.response.use(
|
||||||
(response) => response,
|
(response: AxiosResponse) => response,
|
||||||
async (error) => {
|
async (error: AxiosError) => {
|
||||||
const originalRequest = error.config;
|
const originalRequest = error.config as InternalAxiosRequestConfig & { _retry?: boolean };
|
||||||
|
|
||||||
if (error.response?.status === 401 && originalRequest._retry) {
|
|
||||||
console.error('토큰 재발급 중 401 오류가 발생했습니다. 로그아웃 처리합니다.');
|
|
||||||
alert('세션이 만료되었습니다. 다시 로그인해 주세요.');
|
|
||||||
useAuthStore.getState().clearAuth();
|
|
||||||
window.location.href = '/';
|
|
||||||
return Promise.reject(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error.response?.status === 401 && !originalRequest._retry) {
|
if (error.response?.status === 401 && !originalRequest._retry) {
|
||||||
|
if (isTokenRefreshing) {
|
||||||
|
return new Promise<string | undefined>((resolve, reject) => {
|
||||||
|
failedQueue.push({ resolve, reject });
|
||||||
|
})
|
||||||
|
.then((token) => {
|
||||||
|
if (token && originalRequest.headers) {
|
||||||
|
originalRequest.headers.Authorization = `Bearer ${token}`;
|
||||||
|
}
|
||||||
|
return api(originalRequest);
|
||||||
|
})
|
||||||
|
.catch((err) => Promise.reject(err));
|
||||||
|
}
|
||||||
|
|
||||||
originalRequest._retry = true;
|
originalRequest._retry = true;
|
||||||
|
isTokenRefreshing = true;
|
||||||
|
|
||||||
try {
|
return api
|
||||||
const response = await api.post('/api/auth/reissue', null, {
|
.post('/api/auth/reissue', null, { withCredentials: true })
|
||||||
withCredentials: true,
|
.then((response) => {
|
||||||
});
|
const newAccessToken = response.data?.data?.accessToken;
|
||||||
|
if (!newAccessToken) {
|
||||||
|
throw new Error('Invalid token reissue response');
|
||||||
|
}
|
||||||
|
|
||||||
if (!response.data?.data?.accessToken) {
|
useAuthStore.getState().setLoggedIn(true, newAccessToken);
|
||||||
alert('잘못된 토큰 재발급 응답입니다. 다시 로그인해 주세요.');
|
localStorage.setItem('accessToken', newAccessToken);
|
||||||
|
processQueue(null, newAccessToken);
|
||||||
|
|
||||||
|
if (originalRequest.headers) {
|
||||||
|
originalRequest.headers.Authorization = `Bearer ${newAccessToken}`;
|
||||||
|
}
|
||||||
|
return api(originalRequest);
|
||||||
|
})
|
||||||
|
.catch((reissueError: Error) => {
|
||||||
|
processQueue(reissueError, undefined);
|
||||||
|
console.error('토큰 재발급 실패:', reissueError);
|
||||||
useAuthStore.getState().clearAuth();
|
useAuthStore.getState().clearAuth();
|
||||||
window.location.href = '/';
|
window.location.href = '/';
|
||||||
return Promise.reject(new Error('Invalid token reissue response'));
|
return Promise.reject(reissueError);
|
||||||
}
|
})
|
||||||
|
.finally(() => {
|
||||||
const newAccessToken = response.data.data.accessToken;
|
isTokenRefreshing = false;
|
||||||
|
});
|
||||||
useAuthStore.getState().setLoggedIn(true, newAccessToken);
|
|
||||||
localStorage.setItem('accessToken', newAccessToken);
|
|
||||||
originalRequest.headers.Authorization = `Bearer ${newAccessToken}`;
|
|
||||||
|
|
||||||
return api(originalRequest);
|
|
||||||
} catch (reissueError) {
|
|
||||||
console.error('토큰 재발급 실패:', reissueError);
|
|
||||||
alert('토큰 재발급에 실패했습니다. 다시 로그인해 주세요.');
|
|
||||||
useAuthStore.getState().clearAuth();
|
|
||||||
window.location.href = '/';
|
|
||||||
return Promise.reject(reissueError);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error.response?.status === 400) {
|
if (error.response?.status === 400) {
|
||||||
|
@ -3,7 +3,7 @@ import { useNavigate } from 'react-router-dom';
|
|||||||
import GoogleLogo from '@/assets/icons/web_neutral_rd_ctn@1x.png';
|
import GoogleLogo from '@/assets/icons/web_neutral_rd_ctn@1x.png';
|
||||||
import useAuthStore from '@/stores/useAuthStore';
|
import useAuthStore from '@/stores/useAuthStore';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { fetchProfileApi } from '@/api/authApi';
|
import { fetchProfileApi, reissueTokenApi } from '@/api/authApi';
|
||||||
|
|
||||||
const DOMAIN = 'https://j11s002.p.ssafy.io';
|
const DOMAIN = 'https://j11s002.p.ssafy.io';
|
||||||
|
|
||||||
@ -42,6 +42,19 @@ export default function Home() {
|
|||||||
navigate('/browse');
|
navigate('/browse');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleReissueToken = async () => {
|
||||||
|
try {
|
||||||
|
const response = await reissueTokenApi();
|
||||||
|
console.log('토큰 재발급 성공:', response);
|
||||||
|
alert('토큰 재발급 성공! 새로운 액세스 토큰을 콘솔에서 확인하세요.');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('토큰 재발급 실패:', error);
|
||||||
|
alert('토큰 재발급에 실패했습니다. 다시 시도해 주세요.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const isHidden = true;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full flex-col items-center justify-center bg-gray-50 p-8">
|
<div className="flex h-full flex-col items-center justify-center bg-gray-50 p-8">
|
||||||
<div className="mb-6 max-w-xl rounded-lg bg-white p-6 shadow-lg">
|
<div className="mb-6 max-w-xl rounded-lg bg-white p-6 shadow-lg">
|
||||||
@ -75,13 +88,24 @@ export default function Home() {
|
|||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<Button
|
<>
|
||||||
variant="outlinePrimary"
|
<Button
|
||||||
size="lg"
|
variant="outlinePrimary"
|
||||||
onClick={handleStart}
|
size="lg"
|
||||||
>
|
onClick={handleStart}
|
||||||
시작하기
|
>
|
||||||
</Button>
|
시작하기
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outlinePrimary"
|
||||||
|
size="lg"
|
||||||
|
onClick={handleReissueToken}
|
||||||
|
className="mt-4"
|
||||||
|
style={{ display: isHidden ? 'none' : 'block' }}
|
||||||
|
>
|
||||||
|
리프레시 토큰 재발급 테스트
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user