docs: JWT 토큰 인증 Frontend 구현 트러블슈팅
- Frontend에서 JWT 토큰 전송 구현 - 로그인 체크 및 인증 플로우 개선 - 로그인 후 원래 페이지로 복귀 기능 추가 - 보안 취약점 해결 과정 문서화
This commit is contained in:
parent
b3822e189d
commit
db58e44972
101
troubleshooting/250815_happybell80_JWT인증구현.md
Normal file
101
troubleshooting/250815_happybell80_JWT인증구현.md
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
# 2025년 8월 15일 JWT 인증 구현 작업
|
||||||
|
|
||||||
|
## 오후 7시 20분
|
||||||
|
|
||||||
|
### JWT 토큰 인증 Frontend 구현
|
||||||
|
|
||||||
|
#### 문제 상황
|
||||||
|
- Frontend가 JWT 토큰을 받아서 저장하지만 API 호출 시 전송하지 않음
|
||||||
|
- X-User-Id 헤더만 보내서 누구나 다른 사용자로 위장 가능한 심각한 보안 취약점
|
||||||
|
- 로그인 후 원래 페이지가 아닌 메인(/) 페이지로 리다이렉트되는 문제
|
||||||
|
|
||||||
|
#### 수정 내용
|
||||||
|
|
||||||
|
**1. chat-interface.tsx 수정**
|
||||||
|
```typescript
|
||||||
|
// 추가한 import
|
||||||
|
import { useAuth } from '@/contexts/auth-context';
|
||||||
|
import { LoginDialog } from '@/components/login-dialog';
|
||||||
|
|
||||||
|
// 로그인 체크 추가
|
||||||
|
const { user, isAuthenticated } = useAuth();
|
||||||
|
const [showLoginDialog, setShowLoginDialog] = useState(false);
|
||||||
|
|
||||||
|
// handleSend 함수에 인증 체크
|
||||||
|
if (!isAuthenticated) {
|
||||||
|
setShowLoginDialog(true);
|
||||||
|
toast({
|
||||||
|
title: "로그인 필요",
|
||||||
|
description: "채팅을 하려면 먼저 로그인해주세요.",
|
||||||
|
variant: "default",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 토큰과 userId 전달
|
||||||
|
const token = localStorage.getItem('token');
|
||||||
|
const userId = user?.id || user?.email || 'default';
|
||||||
|
const response = await sendMessage(input, token || '', userId);
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. robing-api.ts 수정**
|
||||||
|
```typescript
|
||||||
|
// 함수 시그니처 변경 (토큰 파라미터 추가)
|
||||||
|
export async function sendMessage(
|
||||||
|
text: string,
|
||||||
|
token: string = '',
|
||||||
|
userId: string = 'test_user'
|
||||||
|
): Promise<MessageResponse> {
|
||||||
|
|
||||||
|
// Authorization 헤더 추가
|
||||||
|
const headers: Record<string, string> = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-User-Id': userId // 호환성을 위해 유지
|
||||||
|
};
|
||||||
|
|
||||||
|
if (token) {
|
||||||
|
headers['Authorization'] = `Bearer ${token}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. auth-context.tsx 수정 (리다이렉트 개선)**
|
||||||
|
```typescript
|
||||||
|
// 로그인 시 현재 경로 저장
|
||||||
|
const login = () => {
|
||||||
|
const currentPath = window.location.pathname;
|
||||||
|
localStorage.setItem('redirectAfterLogin', currentPath);
|
||||||
|
|
||||||
|
// 전체 경로를 OAuth redirect_uri에 포함
|
||||||
|
const redirectUrl = window.location.origin + currentPath;
|
||||||
|
window.location.href = `${AUTH_SERVER_URL}/auth/gmail/login?redirect_uri=${encodeURIComponent(redirectUrl)}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 로그인 성공 후 원래 페이지로 복귀
|
||||||
|
const redirectPath = localStorage.getItem('redirectAfterLogin');
|
||||||
|
if (redirectPath && redirectPath !== window.location.pathname) {
|
||||||
|
localStorage.removeItem('redirectAfterLogin');
|
||||||
|
window.location.href = redirectPath;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 테스트 결과
|
||||||
|
- 로그인하지 않은 상태에서 채팅 시도 시 로그인 다이얼로그 표시 ✅
|
||||||
|
- 로그인 후 Authorization Bearer 토큰 헤더 전송 확인 ✅
|
||||||
|
- /game 페이지에서 로그인 후 /game으로 복귀 ✅
|
||||||
|
|
||||||
|
#### 배포
|
||||||
|
- Git commit & push 완료
|
||||||
|
- Gitea Actions 자동 배포 진행
|
||||||
|
- https://ro-being.com/game 에서 확인 가능
|
||||||
|
|
||||||
|
#### 다음 단계
|
||||||
|
- Backend(robing-gateway)에서 JWT 토큰 검증 로직 추가 필요
|
||||||
|
- 51124 서버 담당자와 협의하여 검증 구현
|
||||||
|
- JWT_SECRET 환경변수 설정 및 공유 필요
|
||||||
|
|
||||||
|
#### 교훈
|
||||||
|
- **보안 취약점은 즉시 수정**: 인증 토큰 미검증은 Critical 이슈
|
||||||
|
- **단계적 접근이 효과적**: Frontend 먼저 수정 후 Backend 적용
|
||||||
|
- **호환성 유지 중요**: X-User-Id 헤더 유지하면서 Authorization 추가
|
||||||
|
- **사용자 경험 고려**: 로그인 후 원래 페이지로 돌아가도록 개선
|
||||||
Loading…
x
Reference in New Issue
Block a user