인증/권한
CSO 정산 포털의 인증 구조와 권한 관리 방법을 설명합니다.
인증 구조
JWT 기반 인증
포털은 Supabase Auth를 사용하지 않고 자체 JWT 인증을 구현합니다.
| 항목 | 값 |
|---|---|
| 라이브러리 | jose (Edge Runtime 호환) |
| 알고리즘 | HS256 |
| 서명 키 | 환경변수 JWT_SECRET |
| 만료 시간 | 24시간 |
| 쿠키 이름 | cso_session |
JWT 페이로드에는 UserSession 객체가 포함됩니다.
{
id, business_number, company_name, email,
is_admin, is_approved, is_test,
must_change_password, profile_complete
}
로그인 절차
- 사용자가 사업자번호(10자리)와 비밀번호를 입력합니다.
- 서버에서 사업자번호를 정규화하고(
-제거), DB에서 회원을 조회합니다. bcrypt로 비밀번호를 검증합니다 (Salt Rounds: 12).- 인증 성공 시 JWT를 생성하고
httpOnly쿠키에 저장합니다.
로그인 실패 처리
| 상태 | 응답 코드 | 메시지 |
|---|---|---|
| 미등록 사업자번호 | 401 | 등록되지 않은 사업자번호입니다 |
| 비밀번호 불일치 | 401 | 실패 횟수 표시 (N/15회) |
| 계정 잠금 (15회 실패) | 423 | 비밀번호 변경 링크 이메일 자동 발송 |
| 승인 대기 | 403 | 관리자 승인 후 로그인 가능 |
계정 잠금 시 등록된 이메일로 비밀번호 재설정 링크가 자동 발송됩니다.
로그인 후 분기
로그인 성공 후 사용자 상태에 따라 리다이렉트됩니다.
| 조건 | 이동 경로 | 설명 |
|---|---|---|
must_change_password: true | /change-password | 관리자가 비밀번호 초기화한 경우 |
profile_complete: false | /complete-profile | 회원정보 미완성 |
| 정상 | /home | 사용자 홈 대시보드 |
세션 관리
쿠키 설정
| 옵션 | 값 | 설명 |
|---|---|---|
httpOnly | true | JavaScript 접근 차단 (XSS 방어) |
secure | 프로덕션만 true | HTTPS 전용 전송 |
sameSite | lax | CSRF 기본 방어 |
maxAge | 86400초 (24시간) | 쿠키 만료 |
path | / | 전체 경로 적용 |
클라이언트 상태 동기화
서버 쿠키와 별도로 localStorage(cso_auth_user 키)에 사용자 정보를 캐싱합니다. AuthContext가 클라이언트 마운트 시 서버 세션(/api/auth/session)과 동기화하여 Hydration 불일치를 방지합니다.
로그아웃
- 서버:
cso_session쿠키 삭제 - 클라이언트:
localStorage항목 삭제 + AuthContext 초기화 - 로그인 페이지로 리다이렉트
관리자 vs 일반회원
권한 차이
| 기능 | 관리자 | 일반회원 |
|---|---|---|
| 정산 조회 범위 | 전체 데이터 | 자기 업체(CSO 매칭 기반)만 |
| 엑셀 다운로드 제한 | 없음 | 일일 5회 |
/admin/* 접근 | 가능 | 접근 불가 |
| 회원 승인/거부 | 가능 | 불가 |
| 정산서 업로드 | 가능 | 불가 |
| 이메일 발송 | 가능 | 불가 |
| 사이트 설정 | 가능 | 불가 |
권한 검증 방식
모든 관리자 API는 요청 시 JWT에서 is_admin 필드를 확인합니다. 관리자가 아닌 경우 403 응답을 반환합니다.
쿠키 → JWT 검증 → UserSession 추출 → is_admin 확인 → 처리 or 403
일반회원의 정산 데이터 접근은 cso_matching 테이블을 통해 필터링됩니다. 회원의 사업자번호와 매칭된 CSO관리업체에 해당하는 정산만 조회됩니다.
회원가입 승인/거부 절차
가입 신청 흐름
- CSO 업체 담당자가 회원가입 폼을 작성합니다.
- 필수: 사업자번호, 업체명, 대표자명, 주소, 연락처, 이메일, 비밀번호
- 사업자번호 중복 검사 자동 수행
- 가입 신청이 접수되면
is_approved: false상태로 DB에 저장됩니다. - 관리자에게 가입 신청 알림 이메일이 발송됩니다.
관리자 승인/거부 (/admin/members)
회원 관리 페이지에서 전체 회원을 조회하고 관리합니다.
통계 카드
페이지 상단에 회원 현황이 카드로 표시됩니다.
- 전체 회원 수
- 승인 대기 수 (주황색 뱃지)
- 승인 완료 수
- 테스트 계정 수
회원 필터링
- 상태별: 전체, 승인대기, 승인완료
- 검색: 업체명, 사업자번호, 이메일로 검색
- 일괄 처리: 체크박스로 여러 회원 선택 후 일괄 승인
승인 처리
- 승인 대기 필터 선택 → 대상 회원 확인
- 승인 버튼 클릭 →
is_approved: true로 업데이트 - 회원에게 승인 완료 이메일 자동 발송
- 일괄 승인 시 이메일 간 200ms 간격으로 순차 발송 (Rate Limit 방어)
거부 처리
- 거부 버튼 클릭 → 거부 사유 입력 다이얼로그 표시
- 사유 입력 후 확인 → 회원에게 거부 이메일 발송 (사유 포함)
- 해당 회원 계정 삭제
기타 관리 기능
| 기능 | 설명 |
|---|---|
| 회원정보 수정 | 업체명, 대표자명, 연락처, 이메일, 관리자 권한 변경 |
| 비밀번호 초기화 | 임시 비밀번호 발급 후 must_change_password 플래그 설정 |
| 회원 삭제 | 확인 다이얼로그 후 삭제 (연관 CSO 매칭 유지) |
| 엑셀 내보내기 | 현재 필터 기준 회원 목록을 엑셀로 다운로드 |
비밀번호 재설정
사용자 요청 (비밀번호 찾기)
- 로그인 페이지에서 비밀번호 찾기 클릭
- 사업자번호 입력 → 등록된 이메일 주소로 재설정 링크 발송
- 링크 유효기간: 30분
- 링크 클릭 → 새 비밀번호 입력 → 변경 완료
관리자 초기화
/admin/members에서 해당 회원의 비밀번호 초기화 클릭- 시스템이 임시 비밀번호를 생성하여 이메일로 발송
- 회원은 다음 로그인 시 비밀번호 변경 페이지로 강제 이동
비밀번호 정책
| 항목 | 일반 계정 | 테스트 계정 |
|---|---|---|
| 최소 길이 | 6자 | 4자 |
| 영문 포함 | 필수 | 불필요 |
| 숫자 포함 | 필수 | 불필요 |
| 해싱 | bcrypt (12 rounds) | 동일 |
Last updated on