Skip to content

Commit

Permalink
feat: Add Authorization Error Handler (kookmin-sw#83)
Browse files Browse the repository at this point in the history
  • Loading branch information
cjeongmin committed May 28, 2024
1 parent e5528a8 commit c9c08ed
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 50 deletions.
103 changes: 54 additions & 49 deletions src/app/lib/axios.ts
Original file line number Diff line number Diff line change
@@ -1,66 +1,71 @@
'use client';

import axios, {
type AxiosError,
type AxiosRequestConfig,
type AxiosResponse,
} from 'axios';
import { useRouter } from 'next/navigation';
import axios, { type AxiosError, type AxiosResponse } from 'axios';

import { getUserData, postTokenRefresh, useAuthActions } from '@/features/auth';
import { load } from '@/shared/storage';
import { postTokenRefresh } from '@/features/auth';
import { load, save } from '@/shared/storage';
import { type FailureDTO } from '@/shared/types';

let isRefreshing = false;
let refreshSubscribers: Array<(token: string) => void> = [];

const subscribeTokenRefresh = (callback: (token: string) => void) => {
refreshSubscribers.push(callback);
};

const onRefreshed = (token: string) => {
refreshSubscribers.forEach(callback => {
callback(token);
});
refreshSubscribers = [];
};

axios.interceptors.response.use(
(response: AxiosResponse) => response,
async (error: AxiosError) => {
const router = useRouter();
const { login, logout, setAuthUserData } = useAuthActions();
async (error: AxiosError<FailureDTO>) => {
if (error.response?.status !== 401 || error.response?.data.code !== 'C002')
return await Promise.reject(error);

const { config, response } = error;
const originalRequest = config as AxiosRequestConfig;

const refreshToken = load<string>({
type: 'local',
key: 'refreshToken',
});
const refreshToken = load<string>({ type: 'local', key: 'refreshToken' });
if (refreshToken == null) {
window.location.href = '/';
return await Promise.reject(error);
}

if (
response != null &&
refreshToken != null &&
response.status === 401 &&
!isRefreshing
) {
isRefreshing = true;
if (isRefreshing) {
return await new Promise(resolve => {
subscribeTokenRefresh((token: string) => {
const { config } = error;
if (config?.headers != null) {
config.headers.Authorization = `Bearer ${token}`;
resolve(axios(config));
}
});
});
}

return await new Promise((resolve, reject) => {
postTokenRefresh(refreshToken)
.then(({ data }) => {
if (originalRequest.headers != null)
originalRequest.headers.Authorization = `Bearer ${data.accessToken}`;
isRefreshing = true;
try {
const {
data: { accessToken, refreshToken: newRefreshToken, expiresIn },
} = await postTokenRefresh(refreshToken);

login(data);
getUserData()
.then(({ data: user }) => {
setAuthUserData(user);
})
.catch(err => {
console.error(err);
});
axios.defaults.headers.common.Authorization = `Bearer ${accessToken}`;
save({ type: 'local', key: 'refreshToken', value: newRefreshToken });
save({ type: 'local', key: 'expiresIn', value: `${expiresIn}` });

resolve(axios(originalRequest));
})
.catch(err => {
router.push('/');
logout();
reject(err);
})
.finally(() => {
isRefreshing = false;
});
});
const { config } = error;
onRefreshed(accessToken);
if (config != null) {
config.headers.Authorization = `Bearer ${accessToken}`;
return await axios(config);
}
} catch (refreshError) {
return await Promise.reject(error);
} finally {
isRefreshing = false;
}

return await Promise.reject(error);
},
);
1 change: 0 additions & 1 deletion src/app/lib/providers/InitializationProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ export function InitialzationProvider({
const { data: userData } = useUserData(auth?.accessToken != null);

useEffect(() => {
console.log(userData);
if (userData != null) {
setAuthUserData(userData);
if (userData.initialized) {
Expand Down

0 comments on commit c9c08ed

Please sign in to comment.