Files
jarca-ledger-mcp/docs/requirements/req-001_dashboard-administrator-authentication.md
T

11 KiB

schema, id, title, revision, status, depends_on, applies_to, language
schema id title revision status depends_on applies_to language
gitea-agent.requirement/v1 REQ-001 Dashboard 관리자 인증 2.0 approved
dashboard
ko

REQ-001 — Dashboard 관리자 인증

1. 목적

Streamlit Dashboard의 모든 금융 데이터 조회·수정·import 기능 앞에 단일 관리자 인증 경계를 두고, 인증되지 않았거나 인증 설정이 유효하지 않은 요청을 fail-closed한다. 인증 성공 후에는 server-signed Secure/HttpOnly cookie로 같은 browser profile의 새 연결과 새 session에서 인증을 정해진 절대 만료까지 복원한다.

2. 배경

Revision 1.0은 Issue #1에서 단일 관리자 credential과 browser-session-only 인증 경계를 도입했다. Issue #6은 sidebar query-link 이동과 browser 재접속에서 반복 로그인이 발생하는 문제를 해결하기 위해 동일한 인증 기능의 session·logout·배포 계약을 persistent cookie 방식으로 확장한다. 새 기능 ID가 아니라 본 canonical 요구사항의 revision으로 관리한다.

3. 범위

포함

  • 모든 Dashboard 페이지 앞의 공통 관리자 인증과 인증 전 DB 차단
  • 환경변수 기반 관리자 ID/password 및 독립적인 signing secret
  • constant-time credential 비교, 일반 오류, 실패당 최소 1초 지연
  • 동일 Dashboard ASGI process의 server-side login/logout HTTP endpoint
  • server-signed Secure/HttpOnly cookie 발급·검증·삭제
  • 기본 24시간, 선택 시 30일인 로그인 성공 시각 기준 절대 만료
  • sidebar 페이지 이동, 새 탭, browser 종료 후 재접속과 새 Streamlit session 복원
  • password/signing-secret/admin-ID rotation에 따른 기존 token 무효화
  • credential·secret·token 비노출과 인증 event 관찰 가능성
  • Compose, Kubernetes Secret, HTTPS와 운영 제약 문서화

제외

  • 다중 사용자, 회원가입, RBAC, 사용자별 데이터 분리
  • OAuth/OIDC, SSO, 외부 IdP, MFA, CAPTCHA, 계정 잠금
  • 외부 인증 proxy/gateway 또는 외부 session middleware로 인증 책임 이전
  • sliding expiration 또는 복원 시 만료 재설정
  • DB-backed token/session registry, 개별 원격 session 폐기 UI
  • MCP endpoint 인증과 MCP transport 변경
  • SQLite 금융 schema와 데이터 모델 변경
  • 다른 browser profile·기기 간 로그인 공유

4. 용어와 기준

  • persistent auth token: version, 관리자 subject, 발급 시각, 절대 만료, token ID만 포함하고 서버가 서명한 bearer token
  • remember me: 기본 미선택 checkbox이며 token TTL을 24시간에서 30일로 선택
  • rotation: 관리자 password 또는 signing secret을 교체하여 기존 token 서명 검증을 실패시키는 운영 절차
  • canonical 근거: Issue #1, Issue #6, source revisions 069d4c16fd84e871e8cd9dc8d9ae39b4fce4a6c6 및 7bea8b368e02e98afd4b2dc463fb270b0e6eb297

5. 요구사항

REQ-001.01 — 공통 인증 경계

Dashboard는 DB path 취득, page dispatch, gateway 호출, upload, rollback 또는 DB-backed 렌더링보다 먼저 현재 설정과 signed cookie를 검증해야 한다. 기존 session boolean만으로 만료·rotation 검증을 우회해서는 안 된다. 실패 시 로그인 화면만 렌더링한다.

REQ-001.02 — 설정과 fail-closed

Credential source는 JARCA_DASHBOARD_ADMIN_ID, JARCA_DASHBOARD_ADMIN_PASSWORD뿐이며 signing source는 JARCA_DASHBOARD_SIGNING_SECRET뿐이다. 누락·빈 값·비문자열·read error 또는 UTF-8 32 byte 미만 signing secret은 일반 설정 오류로 fail-closed한다. Streamlit secrets나 fallback을 사용하지 않는다.

REQ-001.03 — Credential 검증과 비노출

제출 ID와 password는 모두 hmac.compare_digest로 비교한다. Credential, signing secret, signing-key material, token, raw cookie와 Set-Cookie 값은 UI, traceback, URL/query, application log, 문서 또는 st.session_state에 저장하지 않는다. 비민감 correlation ID만 session state에 둘 수 있다.

REQ-001.04 — 실패 로그인 응답

잘못된 ID 또는 password의 각 제출은 처리 시작부터 응답까지 최소 1초가 걸려야 한다. 두 실패는 동일한 오류를 표시하고 cookie·DB·gateway·금융 상태를 변경하지 않는다. 성공에는 실패 지연을 적용하지 않으며 application lock은 두지 않는다.

REQ-001.05 — Persistent session과 절대 만료

정상 로그인도 24시간, 기본 미선택 로그인 유지를 선택한 로그인은 30일의 절대 만료를 사용한다. 발급 시 iat와 exp를 고정하고 page 이동, rerun, 활동, 새 tab, browser 재시작 또는 인증 복원으로 token이나 만료를 갱신하지 않는다. 같은 browser profile의 유효 cookie는 새 Streamlit session에서 인증을 복원한다.

Streamlit 1.58의 ASGI App custom route를 사용해 동일 Dashboard process의 실제 HTTP response에서 cookie를 발급·삭제한다. JavaScript/localStorage/client-side cookie component로 token을 저장하지 않는다. Cookie는 __Host- 이름, Secure, HttpOnly, SameSite=Lax, Path=/, no Domain이며 Max-Age는 token TTL과 같다.

REQ-001.07 — Token 서명·claim·rotation

Token payload는 v, 현재 관리자 ID sub, server iat, exp, 무작위 jti만 허용한다. Signing key는 독립 signing secret과 현재 관리자 password를 domain-separated HMAC-SHA256으로 결합해 파생하고 SHA-256 signer를 사용한다. Signature를 검증한 뒤 exact schema/type, subject, 허용 TTL, 미래 발급 시각과 server-side 만료를 검증한다. Password, signing secret 또는 관리자 ID 변경은 기존 token을 거부해야 한다.

REQ-001.08 — Login/logout HTTP 계약

Login endpoint는 trusted ASGI request scheme과 Origin이 모두 same-origin HTTPS인 form POST만 받고 성공 시 hardened cookie와 root redirect를 반환한다. Logout endpoint도 같은 HTTPS 검증 후 동일 name/path/security 속성과 Max-Age=0으로 browser cookie를 삭제한다. Logout은 금융 DB를 변경하지 않는다. Stateless token 복사본은 개별 폐기하지 않으며 전체 강제 무효화는 password/signing-secret rotation으로 수행한다.

REQ-001.09 — 보호 범위와 기존 기능 호환성

인증 후 기존 page 목록, page query parameter, sidebar 이동, gateway와 MCP 동작은 유지한다. 인증은 Dashboard만 보호하고 MCP endpoint 인증을 의미하지 않는다. 금융 SQLite schema/migration은 변경하지 않는다.

REQ-001.10 — 배포·운영 제약

.env.example, Compose와 Kubernetes는 세 secret setting 이름만 정의하고 실제 값을 저장하지 않는다. 공개 browser origin은 HTTPS여야 한다. TLS proxy를 사용하는 배포는 실제 proxy source IP/CIDR만 Uvicorn forwarded-header allowlist에 넣고 Host/Origin을 보존하며 X-Forwarded-Proto: https를 설정한다. 기본 allowlist는 loopback으로 fail-closed하고 wildcard trust를 금지한다. 최소 1초 delay는 병렬 brute-force를 막지 않으므로 외부 노출 경계에 rate limiting이 필요하다. 모든 verifier replica는 동일 secret과 token 규칙을 사용해야 한다.

REQ-001.11 — 관찰 가능성

Event log는 event name과 비민감 correlation ID만 기록한다. 허용 event에는 설정 오류, 인증 필요, token 수락, login 성공/실패/reject, logout이 포함된다. ID/password/secret/token/cookie/request body와 구체적인 검증 실패 원인은 기록하지 않는다.

6. 인터페이스 요약

  • POST /api/auth/login: admin_id, admin_password, optional remember_me=on
  • POST /api/auth/logout: 입력 credential 없음
  • Cookie: __Host-jarca_dashboard_auth
  • 설정: 세 JARCA_DASHBOARD_* environment variable
  • 상세 계약: docs/interface-contract.md

7. 오류 계약

조건 사용자 결과 Cookie DB/gateway/page
설정 invalid 일반 설정 오류 발급 없음 차단
credential invalid 최소 1초 후 일반 인증 오류 발급 없음 차단
request malformed/cross-site 400/403 발급 없음 차단
cookie 없음·malformed·tampered·expired 로그인 화면 수락 없음 차단
subject/TTL/time/rotation 불일치 로그인 화면 수락 없음 차단
logout root redirect deletion 금융 상태 무변경

8. 수용 기준

  • AC-001 인증 전 모든 DB/gateway/page 기능이 차단된다.
  • AC-002 세 environment setting invalid 상태는 fail-closed한다.
  • AC-003 ID/password를 constant-time 비교하고 일반 오류와 최소 1초 지연을 적용한다.
  • AC-004 정상 login은 24시간 absolute token/cookie를 발급한다.
  • AC-005 기본 미선택 remember-me login은 30일 absolute token/cookie를 발급한다.
  • AC-006 Cookie는 __Host-, Secure, HttpOnly, SameSite=Lax, Path=/, no Domain이다.
  • AC-007 일반 요청·page 이동·복원은 token을 재발급하거나 만료를 연장하지 않는다.
  • AC-008 유효 cookie가 새 Streamlit session과 sidebar query page에서 인증을 복원한다.
  • AC-009 malformed, oversized, tampered, expired, schema-invalid token은 DB 전에 거부된다.
  • AC-010 password, signing secret 또는 관리자 ID rotation이 기존 token을 무효화한다.
  • AC-011 logout은 browser cookie를 삭제하고 금융 상태를 변경하지 않는다.
  • AC-012 credential, secret, token이 session state·URL·log·문서에 노출되지 않는다.
  • AC-013 same-origin HTTPS POST가 아닌 login/logout 요청은 거부된다.
  • AC-014 인증 후 기존 Dashboard page와 MCP behavior가 유지된다.
  • AC-015 실제 loopback HTTPS와 Streamlit WebSocket에서 cookie 발급·복원·삭제를 검증한다.
  • AC-016 금융 DB schema와 data model migration을 추가하지 않는다.
  • AC-017 HTTPS, rate-limit, rotation과 stateless logout 제약이 운영 문서에 기록된다.

9. 검증 매핑

  • Credential/delay/log: tests/test_dashboard_authentication.py
  • Token/TTL/tamper/rotation: tests/test_dashboard_persistent_authentication.py
  • HTTP/cookie/logout: tests/test_dashboard_auth_server.py
  • Streamlit gate/DB ordering/page 복원: tests/test_dashboard_authentication_app.py, tests/test_dashboard_app.py
  • 실제 HTTPS/WebSocket evidence: scripts/persistent_auth_http_evidence.py, docs/evidence/issue-6-persistent-auth-https.json
  • 전체 regression: uv run --extra dashboard --extra dev pytest
  • 정적 검사: uv run --extra dashboard --extra dev ruff check dashboard tests scripts

10. 추적성

  • 최초 요구 Issue: jsfamily/jarca-ledger-mcp#1
  • 지속 로그인 변경 Issue: jsfamily/jarca-ledger-mcp#6
  • 최초 구현 PR: #3
  • Issue #6 구현 PR: 미정
  • Issue #6 기준 revision: 7bea8b368e02e98afd4b2dc463fb270b0e6eb297
  • Issue #6 승인 snapshot body SHA-256: 4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fe