Files
Aislo/docs/wiki/pages/A06_Login/A06_backend.md
T
2026-07-15 18:33:33 +09:00

71 lines
3.7 KiB
Markdown

---
status: stable
page_id: A06_Login
related_pages: ["[[A06_Login/A06_frontend]]", "[[A07_Register/A07_backend]]", "[[A09_Security/A09_backend]]", "[[auth_rbac]]"]
last_updated: 2026-07-12
---
# A06_Login — Backend
세션 쿠키 기반 로그인 + OTP 재인증 + 세션/비밀번호 관리 API.
## 파일 구조
| 파일 | 위치 | 역할 |
|---|---|---|
| `A06_Login_Router.py` | `A06_Login/A06_Login_Router.py:1-179` | 인증 API 라우터 (prefix `/api/auth`) |
| `A06_Login_Schema.py` | `A06_Login/A06_Login_Schema.py:1-20` | 요청 검증 Pydantic 모델 3종 |
## API 엔드포인트
| 엔드포인트 | 위치 | 역할 |
|---|---|---|
| `POST /api/auth/login/request` | `A06_Login/A06_Login_Router.py:51` | 1차 로그인(이메일+비번). 신규 브라우저/주기 재인증 시 `otp_required` 반환 |
| `POST /api/auth/login/verify` | `A06_Login/A06_Login_Router.py:113` | OTP 검증 후 세션 발급 |
| `GET /api/auth/session` | `A06_Login/A06_Login_Router.py:145` | 현재 세션 사용자 조회 (id/email/name/role/company_id/is_master) |
| `POST /api/auth/logout` | `A06_Login/A06_Login_Router.py:160` | 세션 삭제 + 쿠키 제거 |
| `POST /api/auth/password` | `A06_Login/A06_Login_Router.py:170` | 비밀번호 변경 (현재 비번 검증, 재인증 필요 반환) |
## 내부 헬퍼
| 함수 | 위치 | 역할 |
|---|---|---|
| `_user_agent(request)` | `A06_Login/A06_Login_Router.py:40` | User-Agent 헤더 추출(최대 1000자) |
| `_send_otp(user, purpose, label)` | `A06_Login/A06_Login_Router.py:44` | OTP 생성·해시 저장·메일 발송 |
| `_finish_login(user, agent)` | `A06_Login/A06_Login_Router.py:87` | 세션 생성·쿠키 설정·last_login 갱신·로그인 기록 |
## 요청 스키마 (Pydantic)
| 모델 | 위치 | 필드 |
|---|---|---|
| `LoginRequest` | `A06_Login/A06_Login_Schema.py:6` | email(EmailStr), password(8~128자) |
| `OtpVerifyRequest` | `A06_Login/A06_Login_Schema.py:11` | email(EmailStr), otp_code(정규식 `^\d{6}$`) |
| `PasswordChangeRequest` | `A06_Login/A06_Login_Schema.py:16` | current_password, new_password(8~128자), logout_all(기본 True) |
## 로그인 로직 흐름
1. `login/request`: 이메일 조회 → 계정 잠금 확인 → 비밀번호 검증 → 계정 상태 확인(`ACTIVE`/`NO_COMPANY`/`PENDING`만 허용)
2. **OTP 트리거 조건** (둘 중 하나면 OTP 요구):
- 주기적 재인증: `last_email_verified_at``EMAIL_REVERIFY_DAYS` 이전
- 신규 브라우저: `has_known_browser()` 미확인
3. `login/verify`: OTP 검증 → 소비 → `last_email_verified_at` 갱신 → `_finish_login`
4. `_finish_login`: 로그인 실패 초기화 → 브라우저 신뢰 등록 → `auth_expires_at = NOW() + 3개월` → 세션 생성
## 보안 정책 (실 코드 기준)
- **비밀번호 실패**: `record_failed_login()` 누적, 5회 이상 & ADMIN_EMAIL 설정 시 보안 경고 메일 발송
- **계정 잠금**: `account_locked_until`이 미래면 423 반환
- **로그인 기록**: 모든 시도를 `record_login()`으로 기록(성공/실패 사유 포함)
## 의존성 (공통 유틸)
- [[common_util]] — `common_util_auth`(세션/OTP/해시), `common_util_auth_repository`(DB 조회), `common_util_email`(메일 발송)
- 인증/세션/OTP 정책 상세 → [[auth_rbac]]
- `config_system``ADMIN_EMAIL`, `EMAIL_REVERIFY_DAYS`
## ⚠️ 계획서와 실 코드 불일치
[[auth_rbac]]는 계획서 기준(OTP 5분·재시도 3회, 세션 4h/12h, 5회 실패 15분 잠금)으로 기술돼 있으나,
실 코드는 `auth_expires_at = NOW() + 3개월` 갱신·브라우저 신뢰 기반 OTP·5회 실패 시 관리자 알림 메일 방식.
정책 상세 검증 필요 — 규칙 6(실 코드 우선)에 따라 auth_rbac 갱신 대상.