Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,33 @@ export const apiClient = axios.create({
withCredentials: true, // 쿠키 자동 전송 설정
});

// 응답 인터셉터 (에러 처리)
// 임시 하드코딩된 토큰 (쿠키가 없을 때 사용)
const TEMP_ACCESS_TOKEN =
'eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjEsImlhdCI6MTc1NDM4MjY1MiwiZXhwIjoxNzU2OTc0NjUyfQ.BSGuoMWlrzc0oKgSJXHEycxdzzY9-e7gD4xh-wSDemc';
Comment on lines +16 to +18
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

하드코딩된 JWT 토큰을 환경변수로 이동하세요.

프로덕션 환경에서 JWT 토큰이 소스 코드에 하드코딩되어 있으면 보안 위험이 됩니다. 또한 정적 분석 도구에서도 이를 보안 위험으로 탐지하고 있습니다.

다음과 같이 환경변수로 이동하는 것을 권장합니다:

-// 임시 하드코딩된 토큰 (쿠키가 없을 때 사용)
-const TEMP_ACCESS_TOKEN =
-  'eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjEsImlhdCI6MTc1NDM4MjY1MiwiZXhwIjoxNzU2OTc0NjUyfQ.BSGuoMWlrzc0oKgSJXHEycxdzzY9-e7gD4xh-wSDemc';
+// 임시 토큰 (쿠키가 없을 때 사용)
+const TEMP_ACCESS_TOKEN = import.meta.env.VITE_TEMP_ACCESS_TOKEN || '';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// 임시 하드코딩된 토큰 (쿠키가 없을 때 사용)
const TEMP_ACCESS_TOKEN =
'eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjEsImlhdCI6MTc1NDM4MjY1MiwiZXhwIjoxNzU2OTc0NjUyfQ.BSGuoMWlrzc0oKgSJXHEycxdzzY9-e7gD4xh-wSDemc';
// 임시 토큰 (쿠키가 없을 때 사용)
const TEMP_ACCESS_TOKEN = import.meta.env.VITE_TEMP_ACCESS_TOKEN || '';
🧰 Tools
🪛 Gitleaks (8.27.2)

18-18: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


17-18: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🤖 Prompt for AI Agents
In src/api/index.ts around lines 16 to 18, the JWT access token is hardcoded
which is a security risk; remove the literal string and instead read the token
from an environment variable (e.g. process.env.TEMP_ACCESS_TOKEN or a clearer
name like process.env.DEFAULT_ACCESS_TOKEN), validate that the env var exists at
startup and fail fast or log a clear error if missing, and ensure any defaults
are not sensitive values (no hardcoded fallback); update any documentation and
deployment configs to set the new env var.


// Request 인터셉터: 쿠키가 없을 때 임시 토큰을 헤더에 추가
apiClient.interceptors.request.use(
config => {
// 쿠키에서 Authorization 확인
const cookies = document.cookie.split(';');
const hasAuthCookie = cookies.some(cookie => cookie.trim().startsWith('Authorization='));

// 쿠키가 없으면 임시 토큰을 헤더에 추가
if (!hasAuthCookie) {
console.log('🔑 쿠키가 없어서 임시 토큰을 헤더에 추가합니다.');
config.headers.Authorization = `Bearer ${TEMP_ACCESS_TOKEN}`;
} else {
console.log('✅ Authorization 쿠키가 있어서 자동으로 전송됩니다.');
}

return config;
},
error => {
return Promise.reject(error);
},
);
Comment on lines +21 to +40
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

토큰 확인 로직을 개선하세요.

현재 구현에 다음과 같은 문제점이 있습니다:

  1. 쿠키 파싱 로직이 정확하지 않을 수 있습니다 (공백 처리 등)
  2. 빈 임시 토큰에 대한 검증이 없습니다
  3. 로깅이 과도할 수 있습니다

다음과 같이 개선된 구현을 제안합니다:

 // Request 인터셉터: 쿠키가 없을 때 임시 토큰을 헤더에 추가
 apiClient.interceptors.request.use(
   config => {
-    // 쿠키에서 Authorization 확인
-    const cookies = document.cookie.split(';');
-    const hasAuthCookie = cookies.some(cookie => cookie.trim().startsWith('Authorization='));
+    // 쿠키에서 Authorization 확인
+    const getCookie = (name: string) => {
+      const value = `; ${document.cookie}`;
+      const parts = value.split(`; ${name}=`);
+      if (parts.length === 2) return parts.pop()?.split(';').shift();
+      return null;
+    };
+    
+    const authCookie = getCookie('Authorization');

-    // 쿠키가 없으면 임시 토큰을 헤더에 추가
-    if (!hasAuthCookie) {
-      console.log('🔑 쿠키가 없어서 임시 토큰을 헤더에 추가합니다.');
-      config.headers.Authorization = `Bearer ${TEMP_ACCESS_TOKEN}`;
+    // 쿠키가 없으면 임시 토큰을 헤더에 추가
+    if (!authCookie && TEMP_ACCESS_TOKEN) {
+      if (process.env.NODE_ENV === 'development') {
+        console.log('🔑 쿠키가 없어서 임시 토큰을 헤더에 추가합니다.');
+      }
+      config.headers.Authorization = `Bearer ${TEMP_ACCESS_TOKEN}`;
-    } else {
-      console.log('✅ Authorization 쿠키가 있어서 자동으로 전송됩니다.');
+    } else if (authCookie && process.env.NODE_ENV === 'development') {
+      console.log('✅ Authorization 쿠키가 있어서 자동으로 전송됩니다.');
     }

     return config;
   },
   error => {
     return Promise.reject(error);
   },
 );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
apiClient.interceptors.request.use(
config => {
// 쿠키에서 Authorization 확인
const cookies = document.cookie.split(';');
const hasAuthCookie = cookies.some(cookie => cookie.trim().startsWith('Authorization='));
// 쿠키가 없으면 임시 토큰을 헤더에 추가
if (!hasAuthCookie) {
console.log('🔑 쿠키가 없어서 임시 토큰을 헤더에 추가합니다.');
config.headers.Authorization = `Bearer ${TEMP_ACCESS_TOKEN}`;
} else {
console.log('✅ Authorization 쿠키가 있어서 자동으로 전송됩니다.');
}
return config;
},
error => {
return Promise.reject(error);
},
);
apiClient.interceptors.request.use(
config => {
// 쿠키에서 Authorization 확인
const getCookie = (name: string) => {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop()?.split(';').shift();
return null;
};
const authCookie = getCookie('Authorization');
// 쿠키가 없으면 임시 토큰을 헤더에 추가
if (!authCookie && TEMP_ACCESS_TOKEN) {
if (process.env.NODE_ENV === 'development') {
console.log('🔑 쿠키가 없어서 임시 토큰을 헤더에 추가합니다.');
}
config.headers.Authorization = `Bearer ${TEMP_ACCESS_TOKEN}`;
} else if (authCookie && process.env.NODE_ENV === 'development') {
console.log('✅ Authorization 쿠키가 있어서 자동으로 전송됩니다.');
}
return config;
},
error => {
return Promise.reject(error);
},
);
🤖 Prompt for AI Agents
In src/api/index.ts around lines 21 to 40, improve the token-checking logic:
parse document.cookie robustly by splitting on ';' then mapping/trimming each
pair and comparing key names exactly (e.g. cookie.split('=').map(s =>
s.trim())), check if config.headers and config.headers.Authorization already
exist before setting anything (ensure config.headers = config.headers || {}),
only set Authorization to `Bearer ${TEMP_ACCESS_TOKEN}` if no Authorization
cookie and TEMP_ACCESS_TOKEN is a non-empty string, and remove or reduce
console.log noise (use a single debug log or none); also preserve existing
header values and return config as before.


// Response 인터셉터: 401 에러 시 로그인 페이지로 리다이렉트
apiClient.interceptors.response.use(
(response: AxiosResponse) => response,
(error: AxiosError) => {
Expand Down
45 changes: 45 additions & 0 deletions src/components/common/TokenStatus.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useState, useEffect } from 'react';

const TokenStatus = () => {
const [tokenStatus, setTokenStatus] = useState<string>('확인 중...');

useEffect(() => {
const checkToken = () => {
const cookies = document.cookie.split(';');
const hasAuthCookie = cookies.some(cookie => cookie.trim().startsWith('Authorization='));

if (hasAuthCookie) {
setTokenStatus('✅ Authorization 쿠키 있음');
} else {
setTokenStatus('🔑 임시 토큰 사용 중');
}
};

checkToken();
// 5초마다 상태 확인
const interval = setInterval(checkToken, 5000);

return () => clearInterval(interval);
}, []);

return (
<div
style={{
position: 'fixed',
top: '10px',
right: '10px',
background: '#333',
color: 'white',
padding: '8px 12px',
borderRadius: '4px',
fontSize: '12px',
zIndex: 9999,
fontFamily: 'monospace',
}}
>
{tokenStatus}
</div>
);
};

export default TokenStatus;
9 changes: 8 additions & 1 deletion src/hooks/useOAuthToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,15 @@ export const useOAuthToken = () => {
.catch(error => {
console.error('❌ 토큰 발급 실패:', error);
// 에러 발생 시 로그인 페이지로 이동
navigate('/');
// navigate('/');
console.log('💡 임시 토큰을 사용하여 계속 진행합니다.');
// 에러 발생 시에도 임시 토큰으로 계속 진행
// URL에서 code 파라미터 제거
const newUrl = window.location.pathname;
window.history.replaceState({}, document.title, newUrl);
});
} else if (!loginTokenKey) {
console.log('🔑 loginTokenKey가 없습니다. 임시 토큰을 사용합니다.');
}
}, [isTokenRequested, navigate]);

Expand Down