--- type: concept status: stable related_pages: ["[[B01_Dashboard/B01_frontend]]", "[[A06_Login/A06_backend]]", "[[A07_Register/A07_backend]]", "[[architecture/shared_resources]]"] last_updated: 2026-07-17 source: docs/raw/verification/2026-07-17_verify_auth_reverify.md --- # 인증 / RBAC ## 세션 인증 (backend.md 6.3) - 방식: Secure HttpOnly 세션 쿠키 (`session_id`, JWT 아님), `SameSite=Lax` - 저장소: `sessions` 테이블 → [[db_schema/users_auth]] - 유효시간: 활동 기준 4시간 또는 절대 12시간 중 먼저 도달하는 쪽 - DB 풀: `autocommit=True` (세션 즉시 조회 가능하도록) - 프론트: 모든 API 요청에 `credentials: "include"` (frontend.md 5.1) ## OTP / 비밀번호 및 디바이스 신뢰 - OTP 6자리, 5분 유효, 해시 저장(bcrypt/PBKDF2), 재시도 최대 3회(2s/4s/8s 백오프) - 비밀번호 최소 8자, bcrypt(라운드≥12) 또는 PBKDF2, 평문 저장 절대 금지 - 5회 연속 실패 시 계정 임시 잠금 및 ADMIN_EMAIL로 보안 경고 메일 발송 - **디바이스 신뢰 (2026-07-17 도입)**: - 기기 신뢰를 기존 User-Agent 해시에서 **HttpOnly 디바이스 토큰 쿠키 (`device_token`)**로 전환하여 브라우저 업데이트 시 반복적인 OTP 요구를 해결. - 로그인 또는 가입 인증 성공 시 무작위 32바이트 토큰을 발급하여 브라우저 쿠키에 저장(만료 90일)하고, SHA256 해시값은 DB `trusted_devices` 테이블에 저장. - 기기 신뢰 만료 정책: `last_used_at`이 `EMAIL_REVERIFY_DAYS`(90일) 이내인 기기 토큰만 유효 처리하며, 로그인 시 `last_used_at`을 자동으로 갱신. ## 사용자 상태 생명주기 ``` PENDING_EMAIL → NO_COMPANY → (PENDING | ACTIVE) → (INACTIVE | REJECTED) ``` | 상태 | 접근 가능 범위 | |---|---| | NO_COMPANY | 로그인 가능, B01만 (B02~B11 차단) | | PENDING | 로그인 가능, B01만 (승인 대기) | | ACTIVE | 전체 (B01 + B02~B11) | ## 역할 (users.role) | 역할 | 전체회사조회 | 역할변경 | 팀원관리 | 가입승인 | 프로젝트조회 범위 | |---|---|---|---|---|---| | SYSTEM_ADMIN | ✅ | ✅ | ✅ | ✅ | 전체 | | ADMIN | ✗ | ✗ | ✅(자기회사) | ✅(자기회사) | 회사별 | | USER | ✗ | ✗ | ✗ | ✗ | 회사별 | `is_master` 플래그: TRUE=회사 생성자/ADMIN(팀원 승인권), FALSE=일반 팀원 ## 권한 검증 헬퍼 (B01_Dashboard) | 항목 | 위치 | 역할 | |---|---|---| | `_can_edit_project()` | `B01_Dashboard_Router.py:79` | 역할·회사 기준 프로젝트 수정 권한 | | `_can_manage_automation()` | `B01_Dashboard_Router.py:87` | USER 차단과 프로젝트 범위 검사 | | `_can_edit_user()` | `B01_Dashboard_Router.py:93` | 사용자 수정 범위 검사 | | `count_company_admins()` | `B01_Dashboard_Repository.py:584` | 마지막 ADMIN 삭제·강등 방지 | | `canEditProject()` 외 5개 | `B01_Dashboard_UI_Helper.ts:3` | 프론트 표시 제어; 백엔드 권한을 대체하지 않음 | **원칙:** 백엔드가 모든 수정/삭제/역할변경 API에서 반드시 재검증 (프론트 UI는 표시 제어만, 신뢰 금지). 위반 시 403 `{"status":"error","message":"Access denied"}`. ## 인증 갱신 및 만료 일원화 - 기존의 중복된 `auth_expires_at` 컬럼 및 로직을 제거하고, 만료 개념을 **`EMAIL_REVERIFY_DAYS`(90일)로 일원화**하였습니다. - 로그인/가입 인증 이후 90일이 지나면 OTP 재인증을 요구하며, 대시보드 표시 시에도 `last_email_verified_at + 90일`을 계산하여 적용합니다. ## 라우팅 가드 (frontend.md 5.2) B02~B11 진입 시 `fetchDashboardMe()` → `company_id`+`status` 확인 → NO_COMPANY/PENDING이면 B01로 강제 리다이렉트. ## 사용처 (역참조) - [[B01_Dashboard/B01_frontend]] — 권한 헬퍼 5종, RBAC 접근제어 및 만료일 동적 계산 표시 - [[A06_Login/A06_backend]] — 세션 발급/OTP 재인증, 디바이스 토큰 쿠키 신뢰 판정 및 발급 - [[A07_Register/A07_backend]] — 가입 OTP 인증 성공 시 기기 신뢰 연계 등록 - [[A09_Security/A09_backend]] — require_master/require_system_admin 권한 데코레이터