11# 인증 및 접근 제어
22
3- > 📌 ** 핵심 결정:**
4- > → ` architecture/decisions.md § 결정 #2: 관리자 인증 ( OAuth + PIN) `
3+ > 📌 ** 핵심 결정:**
4+ > → ` architecture/01- decisions.md § 결정 #2: 로컬 로그인 우선 + 선택 OAuth + 관리자 PIN `
55
6- ** 최종 업데이트:** 2025-01-29
6+ ** 최종 업데이트:** 2026-02-22
77
88---
99
10- ## 인증 방식
11-
12- ### Google OAuth 2.0
13-
14- ** 일반 로그인:**
15- - Google 계정으로 로그인
16- - 일상적인 사용
17- - Whitelist 기반 접근 제어
18-
19- ** 설정 방법:**
20- 1 . Google Cloud Console 접속
21- 2 . OAuth 2.0 클라이언트 ID 생성
22- 3 . 리다이렉트 URI 등록: ` {YOUR_DOMAIN}/auth/callback `
23- 4 . Client ID와 Secret을 설정에 입력
10+ ## 인증 방식
11+
12+ ### 인증 우선순위
13+
14+ 1 . ** 이메일 + 비밀번호 로그인 (기본/필수)**
15+ 2 . ** TOTP 2차 인증 (권장, 관리자 필수 권장)**
16+ 3 . ** Passkey (선택, Passwordless 지원)**
17+ 4 . ** Google OAuth (선택, 편의 로그인)**
18+
19+ ### 계정 복구 우선순위
20+
21+ 1 . ** Self-service 복구 (SMTP 활성화 시)**
22+ 2 . ** Admin-assisted 복구 (1회용 토큰 발급)**
23+ 3 . ** Mode 1 복구 (Local CLI reset, 최후 수단)**
24+
25+ > ⚠️ ** 범위 명시:**
26+ > Synology Account와 같은 외부 계정 복구는 Fieldstack 기본 범위에서 제외합니다.
27+ > Fieldstack는 로컬 인스턴스 기준 복구(웹/관리자/CLI)만 제공합니다.
28+
29+ ### 1) 이메일 + 비밀번호 (기본)
30+
31+ ** 일반 로그인 기본값:**
32+ - 사용자 식별자는 이메일 사용
33+ - 비밀번호는 Argon2id 해시로 저장
34+ - Whitelist 기반 접근 제어
35+
36+ ### 2) TOTP 2차 인증 (Google Authenticator 등)
37+
38+ ** 적용 방식:**
39+ - 로그인 1차 성공 후 OTP 6자리 입력 (Step-up)
40+ - 2FA 등록은 계정 설정에서 ON/OFF
41+ - 관리자 계정은 2FA 필수 권장
42+
43+ ### 3) Passkey (Passwordless)
44+
45+ ** 지원 목표:**
46+ - WebAuthn 기반 로그인 지원
47+ - 비밀번호 없이 기기 인증(지문/Face ID/보안키) 가능
48+ - 필요 시 2차 인증 정책과 병행 가능
49+
50+ ### 4) Google OAuth 2.0 (선택)
51+
52+ ** 역할:**
53+ - 기본 로그인 수단이 아닌 추가 편의 로그인
54+ - 로컬 계정과 연결(Link)하여 사용
55+
56+ ** 설정 방법:**
57+ 1 . Google Cloud Console 접속
58+ 2 . OAuth 2.0 클라이언트 ID 생성
59+ 3 . 리다이렉트 URI 등록: ` {YOUR_DOMAIN}/auth/callback `
60+ 4 . Client ID와 Secret을 설정에 입력
2461
2562---
2663
2764## 관리자 인증 (PIN)
2865
2966> 💡 ** 왜 PIN을 선택했나요?**
30- > → ` architecture/decisions.md § 결정 #2 ` - 설계 근거 참고
67+ > → ` architecture/01- decisions.md § 결정 #2 ` - 설계 근거 참고
3168
3269### 개념
3370
3471** 이중 인증 구조:**
3572```
36- 일반 사용:
37- Google OAuth → 앱 접근
38-
39- 관리자 설정 접근:
40- Google OAuth → 앱 접근
41- +
42- 4~6자리 PIN → 관리자 설정 접근
73+ 일반 사용:
74+ 1차 로그인(이메일+비밀번호 또는 Passkey/ OAuth) → 앱 접근
75+
76+ 관리자 설정 접근:
77+ 1차 로그인(이메일+비밀번호 또는 Passkey/ OAuth) → 앱 접근
78+ +
79+ 4~6자리 PIN → 관리자 설정 접근
4380```
4481
4582### 용도
6097### 사용 시나리오
6198
6299```
63- 1. Google로 이미 로그인된 상태
100+ 1. 1차 로그인 완료 상태
101+ (이메일+비밀번호 또는 Passkey/OAuth)
64102 ↓
651032. 관리자 설정 페이지 접근 시도
66104 (예: 사용자 관리, DB 설정, 시스템 설정)
103141
104142** 작동 방식:**
105143```
106- 1. 사용자가 Google로 로그인
144+ 1. 사용자가 1차 로그인
145+ (이메일+비밀번호 또는 Passkey/OAuth)
107146 ↓
1081472. 이메일 주소 확인
109148 ↓
@@ -127,31 +166,41 @@ admin_sessions 테이블은 관리자 PIN 인증 후 생성되는 임시 세션
127166
128167## 인증 플로우
129168
130- ### 1. 로그인 프로세스
131-
132- ```
133- 사용자 → "Google로 로그인" 버튼 클릭
134- ↓
135- Google OAuth 페이지로 리다이렉트
136- ↓
137- 사용자가 Google 계정으로 로그인
138- ↓
139- 권한 승인 (프로필, 이메일)
140- ↓
141- Callback URL로 리다이렉트 (Authorization Code)
142- ↓
143- Backend에서 Code → Access Token 교환
144- ↓
145- Google API로 사용자 정보 조회
146- ↓
147- 이메일 주소 Whitelist 확인
148- ↓
149- JWT 토큰 발급 → 클라이언트에 전달
150- ↓
151- 메인 화면으로 이동
152- ```
153-
154- ### 2. 세션 관리
169+ ### 1. 로그인 프로세스 (기본: 이메일 + 비밀번호 + 선택 2FA)
170+
171+ ```
172+ 사용자 → 이메일/비밀번호 입력
173+ ↓
174+ Backend에서 비밀번호 검증 (Argon2id)
175+ ↓
176+ 계정의 2FA 활성 여부 확인
177+ ↓
178+ [2FA OFF] JWT 발급 → 메인 화면 이동
179+ ↓
180+ [2FA ON] OTP 6자리 입력 화면 표시
181+ ↓
182+ TOTP 검증 성공
183+ ↓
184+ JWT 발급 → 메인 화면 이동
185+ ```
186+
187+ ### 1-1. Google OAuth 로그인 (선택)
188+
189+ ```
190+ 사용자 → "Google로 로그인" 버튼 클릭
191+ ↓
192+ Google OAuth 페이지 리다이렉트
193+ ↓
194+ Callback 처리 후 이메일 확인
195+ ↓
196+ 로컬 계정과 연결된 사용자 매핑
197+ ↓
198+ (2FA 활성 계정이면 OTP 추가 검증)
199+ ↓
200+ JWT 발급 → 메인 화면 이동
201+ ```
202+
203+ ### 2. 세션 관리
155204
156205** JWT 기반:**
157206
@@ -163,7 +212,46 @@ JWT 토큰의 내부 구조(Payload)는 다음과 같습니다. 사용자의 고
163212
164213** 만료 시간:**
165214- Access Token: 7일
166- - Refresh Token: 30일 (선택)
215+ - Refresh Token: 30일 (선택)
216+
217+ ### 3. 비밀번호 분실 복구 프로세스
218+
219+ ```
220+ [경로 A] SMTP 활성화
221+ 사용자 → "비밀번호를 잊으셨나요" 요청
222+ ↓
223+ 서버 → 계정 존재 여부와 무관하게 동일 응답
224+ ↓
225+ 단일 사용 토큰(만료 15분) 발급 및 이메일 전송
226+ ↓
227+ 사용자 → 새 비밀번호 설정
228+ ↓
229+ 모든 기존 세션/리프레시 토큰 무효화
230+
231+ [경로 B] SMTP 비활성화
232+ 사용자 → 관리자에게 복구 요청
233+ ↓
234+ 관리자 → 1회용 복구 토큰 발급 (짧은 만료)
235+ ↓
236+ 사용자 → 토큰으로 비밀번호 재설정
237+ ↓
238+ 모든 기존 세션/리프레시 토큰 무효화
239+
240+ [경로 C] Mode 1 (최후 수단)
241+ 관리자(서버 로컬 접근 가능) → Local CLI reset 실행
242+ ↓
243+ 대상 계정 비밀번호 재설정/강제 변경
244+ ↓
245+ 모든 기존 세션/리프레시 토큰 무효화
246+ ```
247+
248+ ### 4. Mode 1 (Local CLI reset) 정의
249+
250+ - Synology Mode 1 개념을 Fieldstack 운영에 맞게 적용한 비상 복구 절차
251+ - 서버 로컬 접근 권한이 있는 운영자만 실행 가능
252+ - 데이터는 유지하고 인증 정보만 복구
253+ - 실행 후 대상 계정은 다음 로그인에서 비밀번호 변경을 강제할 수 있음
254+ - 감사 로그에 실행자, 대상 계정, 실행 시간, 실행 IP를 기록
167255
168256---
169257
@@ -176,7 +264,7 @@ authMiddleware는 모든 보호된 API 요청에 적용되는 인증 미들웨
176264### 관리자 PIN 인증
177265
178266> 💡 ** 구현 상세:**
179- > → ` architecture/decisions.md § 결정 #2: 기술 구현 `
267+ > → ` architecture/01- decisions.md § 결정 #2: 기술 구현 `
180268
181269AdminAuthService 클래스가 관리자 PIN 인증의 전체 흐름을 담당합니다.
182270
@@ -203,7 +291,7 @@ verifySession 메서드는 세션 ID로 세션을 조회한 후, 존재하지
203291### PIN 입력 컴포넌트
204292
205293> 💡 ** UI 구현 예시:**
206- > → ` architecture/decisions.md § 결정 #2: UI 구현 `
294+ > → ` architecture/01- decisions.md § 결정 #2: UI 구현 `
207295
208296PinInput 컴포넌트는 사용자가 PIN을 입력하는 화면을 제공합니다. length 속성으로 PIN 자릿수(4 또는 6)를 설정할 수 있습니다.
209297
@@ -226,7 +314,7 @@ ProtectedAdminRoute 컴포넌트는 관리자 전용 페이지를 보호하는
226314## 보안 고려사항
227315
228316> 📖 ** 전체 보안 정책:**
229- > → ` architecture/decisions.md § 결정 #2: 보안 `
317+ > → ` architecture/01- decisions.md § 결정 #2: 보안 `
230318
231319### Rate Limiting
232320
@@ -246,7 +334,7 @@ logFailedAttempt 함수는 PIN 인증 실패 시 사용자 ID, 실패 액션, IP
246334
247335---
248336
249- ## Google OAuth 콜백 처리
337+ ## Google OAuth 콜백 처리 (선택 기능)
250338
251339GET /auth/google 엔드포인트는 사용자를 Google의 OAuth 인증 페이지로 리다이렉트합니다. 프로필과 이메일 정보에 대한 권한을 요청합니다.
252340
@@ -264,27 +352,30 @@ GET /auth/callback 엔드포인트는 Google에서 돌아온 콜백을 처리합
264352
265353## 📚 관련 문서
266354
267- - 📌 ` architecture/decisions.md § 결정 #2 ` - PIN 방식 선택 근거
268- - 📖 ` deployment/setup-wizard.md ` - 초기 관리자 설정
355+ - 📌 ` architecture/01- decisions.md § 결정 #2 ` - PIN 방식 선택 근거
356+ - 📖 ` deployment/02- setup-wizard.md ` - 초기 관리자 설정
269357- 📖 ` community/02-github-policy.md ` - 보안 정책
270358
271359---
272360
273361## FAQ
274362
275- ### Q1. 왜 비밀번호가 아니라 PIN인가요?
276- ** A:** 홈서버 환경에서는 스마트폰 잠금처럼 간단한 PIN이 더 적합합니다. 복잡한 비밀번호는 자주 입력해야 하는 관리자 설정에 부담이 됩니다 .
363+ ### Q1. 왜 관리자 비밀번호가 아니라 PIN인가요?
364+ ** A:** 일반 로그인은 이메일+비밀번호(또는 Passkey)를 사용하고, 관리자 설정 접근만 추가 단계로 PIN을 요구합니다. 홈서버 환경에서 관리자 설정 진입 시 빠른 입력 UX를 유지하기 위한 선택입니다 .
277365
278- > 📖 상세 이유: ` architecture/decisions.md § 결정 #2 `
366+ > 📖 상세 이유: ` architecture/01- decisions.md § 결정 #2 `
279367
280368### Q2. PIN이 안전한가요?
281369** A:** PBKDF2 + Rate Limiting으로 충분히 안전합니다. 5회 실패 시 5분 잠금되므로 브루트포스 공격이 어렵습니다.
282370
283- ### Q3. PIN을 잊어버렸어요!
284- ** A:** 데이터베이스에서 직접 초기화해야 합니다. 백업 관리자 계정을 미리 만들어두는 것을 권장합니다 .
371+ ### Q3. PIN을 잊어버렸어요!
372+ ** A:** Mode 1(Local CLI reset)으로 복구합니다. 서버 로컬 접근 권한이 있는 운영자가 CLI로 PIN 또는 비밀번호를 재설정하고, 이후 세션을 무효화합니다 .
285373
286374### Q4. 세션이 자주 만료돼요
287375** A:** 30분 타임아웃은 보안을 위한 것입니다. 설정에서 조정 가능합니다 (권장하지 않음).
288376
289- ### Q5. 일반 사용자도 PIN이 필요한가요?
290- ** A:** 아니요. PIN은 관리자 설정에만 필요합니다. 일반 사용자는 Google OAuth만으로 충분합니다.
377+ ### Q5. 일반 사용자도 PIN이 필요한가요?
378+ ** A:** 아니요. PIN은 관리자 설정 접근에만 필요합니다. 일반 사용자는 이메일+비밀번호(기본) 또는 선택 로그인(OAuth/Passkey)으로 사용합니다.
379+
380+ ### Q6. 비밀번호를 잊어버렸어요!
381+ ** A:** SMTP가 활성화된 경우 self-service 재설정 링크를 사용합니다. SMTP가 없으면 관리자 발급 1회용 토큰으로 복구하고, 마지막 수단은 Mode 1(Local CLI reset)입니다.
0 commit comments