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

17 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. 목적

Dashboard의 금융 기능 앞에 단일 fail-closed 관리자 인증 경계를 두고, 유효한 인증 cookie가 같은 browser profile의 새 연결에서 복원되도록 한다.

2. 배경

Issue #1의 관리자 인증을 Issue #6에서 persistent signed cookie와 명시적 logout으로 확장한다. 기존 요구사항의 정체성, 승인 범위, Issue #1과 구현 PR #3의 추적성을 유지하며 revision 2.0으로 갱신한다.

3. 범위

포함

  • Dashboard 전 기능 앞의 인증 gate와 인증 전 DB 차단
  • JARCA_DASHBOARD_ADMIN_ID, JARCA_DASHBOARD_ADMIN_PASSWORD, JARCA_DASHBOARD_SIGNING_SECRET 기반 server-only 검증
  • constant-time credential 비교, 일반 오류, 실패당 최소 1초 지연
  • same-origin HTTPS login, cleanup, logout route
  • Secure/HttpOnly/SameSite=Lax/Path=/ signed cookie와 24시간·30일 absolute TTL
  • 고정된 비민감 role subject와 설정 credential에 묶인 서명 검증
  • invalid, expired, tampered, malformed, configuration-failure cookie의 cleanup 및 matching deletion
  • password, signing-secret, admin-ID 변경에 따른 기존 token 거부
  • HTTPS, proxy, Kubernetes/Compose 운영 제약과 민감정보 비노출

제외

  • 다중 사용자, RBAC, OAuth/OIDC, SSO, MFA, CAPTCHA, 계정 잠금
  • 외부 인증 proxy로 책임 이전
  • sliding expiration, DB-backed session/token registry, per-token revocation
  • MCP endpoint 인증 변경, SQLite schema/data migration
  • 다른 browser profile·기기 간 로그인 공유

4. 용어와 기준

  • role subject: token의 sub에 고정 기록하는 비민감 값 dashboard-admin; 관리자 ID나 credential이 아니다.
  • server-only binding: signing secret과 현재 설정 credential을 서버에서만 읽어 key를 파생하며 browser에 값을 보내지 않는 방식.
  • cleanup: 인증 실패 cookie를 같은 origin의 cleanup route에서 matching name/path/security 속성으로 삭제하는 절차.
  • canonical 근거: Issue #1, Issue #6, 구현 PR #3 및 #7.

5. 요구사항

REQ-001.01 — 공통 인증 경계

모든 DB path, page dispatch, gateway, upload, import보다 먼저 설정과 cookie를 검증하고, 실패하면 로그인 화면만 표시한다.

영향 범위

  • 인터페이스: dashboard-auth-gate
  • 영속성: 없음
  • 후속 사용 주체: Dashboard page와 gateway
  • 마이그레이션: 없음
  • 호환성: 인증 후 기존 page/query 동작 유지
  • 배포 전제 조건: 유효한 세 환경변수

REQ-001.02 — 설정과 server-only binding

세 환경변수만 사용하고 누락·빈 값·read error·짧은 signing secret이면 fail-closed한다. signing key는 서버에서 configured credential과 secret으로만 파생한다.

영향 범위

  • 인터페이스: dashboard-auth-config, dashboard-auth-cookie
  • 영속성: 없음
  • 후속 사용 주체: 인증 verifier
  • 마이그레이션: 없음
  • 호환성: Streamlit secrets/fallback 미사용
  • 배포 전제 조건: secret manager 주입

REQ-001.03 — Credential과 민감정보 비노출

ID와 password는 constant-time 비교한다. credential, secret, signing material, token, raw cookie와 Set-Cookie 값은 UI, URL, log, 문서, session state에 기록하지 않는다.

영향 범위

  • 인터페이스: login form, event log
  • 영속성: 없음
  • 후속 사용 주체: 운영자와 감사 도구
  • 마이그레이션: 없음
  • 호환성: 비민감 correlation ID만 유지
  • 배포 전제 조건: redacted logging

REQ-001.04 — Login 실패 계약

ID 또는 password가 틀리면 동일한 일반 오류와 최소 1초 지연을 적용하고 상태를 변경하지 않는다. 성공에는 지연을 적용하지 않는다.

영향 범위

  • 인터페이스: POST /api/auth/login
  • 영속성: 없음
  • 후속 사용 주체: browser
  • 마이그레이션: 없음
  • 호환성: 기존 login form 유지
  • 배포 전제 조건: 외부 rate limiting

REQ-001.05 — Persistent token

성공 login은 기본 24시간, remember-me 선택 시 30일의 absolute TTL token을 발급한다. iat와 exp는 고정하며 활동·rerun·복원으로 연장하지 않는다. sub는 항상 dashboard-admin이다.

영향 범위

  • 인터페이스: dashboard-auth-cookie
  • 영속성: browser cookie만
  • 후속 사용 주체: 새 Streamlit session
  • 마이그레이션: 기존 session boolean에서 cookie로 전환
  • 호환성: 같은 browser profile 복원
  • 배포 전제 조건: HTTPS

Cookie 이름은 __Host-jarca_dashboard_auth이며 Secure, HttpOnly, SameSite=Lax, Path=/, no Domain을 사용한다. 일반 요청은 재발급하지 않는다.

영향 범위

  • 인터페이스: HTTP response와 browser
  • 영속성: browser cookie
  • 후속 사용 주체: browser/Streamlit handshake
  • 마이그레이션: 없음
  • 호환성: host-only cookie
  • 배포 전제 조건: 공개 origin HTTPS

없음·malformed·oversized·tampered·expired·claim-invalid·rotation-invalid 또는 configuration-failure cookie는 보호 자원에 접근하기 전에 거부한다. 같은 origin cleanup route를 통해 동일 name/path/security 속성과 Max-Age=0 deletion을 반환한 뒤 unauthenticated page를 표시한다.

영향 범위

  • 인터페이스: GET /api/auth/cleanup, dashboard-auth-gate
  • 영속성: 서버 session/registry 없음
  • 후속 사용 주체: browser
  • 마이그레이션: 없음
  • 호환성: stale cookie가 남지 않음
  • 배포 전제 조건: same-origin HTTPS

REQ-001.08 — Login, cleanup, logout

Login과 logout은 trusted same-origin HTTPS POST만 허용하고 cleanup GET은 same-origin HTTPS Referer를 검증한다. logout은 명시적으로 matching deletion을 반환한다. cleanup과 logout은 금융 DB를 변경하지 않는다.

영향 범위

  • 인터페이스: POST /api/auth/login, GET /api/auth/cleanup, POST /api/auth/logout
  • 영속성: 없음
  • 후속 사용 주체: browser
  • 마이그레이션: 없음
  • 호환성: root redirect 유지
  • 배포 전제 조건: proxy가 Host/Origin/Proto 보존

REQ-001.09 — Rotation과 무상태성

Token claim은 v, 고정 sub, iat, exp, jti만 허용한다. password, signing secret 또는 admin ID 변경은 기존 token을 거부한다. per-token revocation이나 DB session은 만들지 않는다.

영향 범위

  • 인터페이스: token verifier
  • 영속성: revocation/session 저장소 없음
  • 후속 사용 주체: 운영 rotation 절차
  • 마이그레이션: 없음
  • 호환성: 전체 무효화는 rotation으로 수행
  • 배포 전제 조건: 모든 replica 동일 설정

REQ-001.10 — 보호 범위와 운영

인증 후 기존 page, query parameter, gateway와 MCP 동작을 유지한다. Compose/Kubernetes는 실제 secret을 저장하지 않으며 HTTPS, forwarded-header allowlist와 rate limit을 적용한다.

영향 범위

  • 인터페이스: Dashboard 운영 진입점
  • 영속성: 기존 SQLite만
  • 후속 사용 주체: 운영 배포
  • 마이그레이션: DB migration 없음
  • 호환성: MCP endpoint는 별도 계약
  • 배포 전제 조건: TLS와 제한된 proxy CIDR

REQ-001.11 — 관찰 가능성

설정 오류, 인증 필요, token 수락, login 성공/실패/reject, cleanup, logout event와 비민감 correlation ID만 기록한다. 민감 값과 구체적 검증 원인은 기록하지 않는다.

영향 범위

  • 인터페이스: event log
  • 영속성: 로그에 비민감 메타데이터만
  • 후속 사용 주체: 운영 관찰 도구
  • 마이그레이션: 없음
  • 호환성: 일반 오류 메시지 유지
  • 배포 전제 조건: redaction 정책

6. 제약과 불변조건

  • sub는 비민감 고정 role 값이며 credential에서 파생하거나 관리자 ID를 담지 않는다.
  • credential, secret, token, cookie 값은 저장·출력·문서화하지 않는다.
  • 인증 성공 전 DB/gateway/page dispatch는 실행하지 않는다.
  • 만료는 absolute이며 활동으로 연장하지 않는다.
  • invalid cookie cleanup과 explicit logout의 deletion은 발급 cookie와 matching scope를 사용한다.
  • 서버는 per-token revocation과 DB-backed session을 유지하지 않는다.

7. 인수 기준

REQ-001.01-AC01 — 인증 전 차단

판정 기준

모든 인증 거부 경로에서 DB, gateway, page dispatch가 실행되지 않는다.

검증

AppTest와 gate ordering 테스트에서 database access sentinel이 호출되지 않음을 확인한다.

검증 분류

  • 수준: integration
  • 백엔드: Streamlit AppTest
  • 케이스: missing config, no cookie, invalid cookie

상태 불변조건

거부 시 금융 상태와 DB는 변경되지 않는다.

REQ-001.02-AC01 — 설정 fail-closed

판정 기준

세 설정 중 하나라도 유효하지 않으면 일반 설정 오류로 인증을 거부한다.

검증

설정별 누락·빈 값·짧은 secret 테스트를 실행한다.

검증 분류

  • 수준: unit/integration
  • 백엔드: pytest
  • 케이스: 각 환경변수 누락, 빈 값, short secret

상태 불변조건

cookie 발급과 보호 자원 접근이 없다.

REQ-001.03-AC01 — 민감정보 비노출

판정 기준

credential, secret, token, cookie가 UI·URL·log·session state에 나타나지 않는다.

검증

로그·응답·session state와 문서를 redaction 검색한다.

검증 분류

  • 수준: static/integration
  • 백엔드: pytest, ruff, repository search
  • 케이스: 성공·실패 login과 invalid cookie

상태 불변조건

비민감 correlation ID 외 인증 비밀 상태가 없다.

REQ-001.03-AC02 — 두 credential의 constant-time 비교

판정 기준

제출 ID와 password는 각각 hmac.compare_digest로 비교하며, 한 필드가 일치하지 않아도 다른 필드의 비교를 생략하지 않는다.

검증

tests/test_dashboard_authentication.py::test_verify_credentials_uses_constant_time_comparison_for_both_fields에서 성공 및 두 필드가 모두 잘못된 경우에 두 비교의 호출 순서와 인자를 계측한다.

검증 분류

  • 수준: unit
  • 백엔드: pytest monkeypatch
  • 케이스: 정상 credential, 잘못된 ID와 password

상태 불변조건

일반 동등 비교나 한 필드 실패에 따른 short-circuit를 사용하지 않는다.

REQ-001.04-AC01 — 실패 지연과 일반 오류

판정 기준

잘못된 ID와 잘못된 password가 같은 일반 오류를 반환하고 각각 최소 1초 지연된다.

검증

두 실패 경로의 elapsed time과 response를 측정한다.

검증 분류

  • 수준: integration
  • 백엔드: pytest/HTTP client
  • 케이스: wrong ID, wrong password

상태 불변조건

cookie·DB·gateway 상태가 변하지 않는다.

REQ-001.05-AC01 — Absolute TTL과 고정 subject

판정 기준

기본 24시간과 remember-me 30일 token의 iat, exp, sub=dashboard-admin이 고정된다.

검증

token claim, TTL, rerun/non-sliding 테스트를 실행한다.

검증 분류

  • 수준: unit/integration
  • 백엔드: pytest
  • 케이스: normal, remember-me, activity, reconnect

상태 불변조건

복원은 token을 갱신하지 않는다.

판정 기준

발급 cookie가 이름·Secure·HttpOnly·SameSite·Path·Domain 계약을 모두 만족한다.

검증

실제 HTTP Set-Cookie를 파싱하고 속성을 검사한다.

검증 분류

  • 수준: integration
  • 백엔드: Starlette TestClient와 loopback HTTPS
  • 케이스: login normal/remember

상태 불변조건

Domain attribute가 없고 평문 전송에서 발급되지 않는다.

판정 기준

invalid/expired/tampered/config-failure cookie는 보호 자원 전에 거부되고 same-origin cleanup route의 matching deletion 후 unauthenticated page가 표시된다.

검증

실제 browser/HTTP 흐름에서 각 invalid cookie와 cleanup response를 확인한다.

검증 분류

  • 수준: integration/browser
  • 백엔드: HTTPS probe와 Streamlit browser evidence
  • 케이스: malformed, expired, tampered, wrong claims, config failure

상태 불변조건

cleanup은 DB를 변경하지 않으며 stale cookie가 남지 않는다.

REQ-001.08-AC01 — Explicit logout

판정 기준

logout POST가 matching Max-Age=0 deletion을 반환하고 다음 접근이 unauthenticated다.

검증

login 후 logout response와 fresh request를 확인한다.

검증 분류

  • 수준: integration/browser
  • 백엔드: HTTPS probe
  • 케이스: same-origin logout, cross-origin rejection

상태 불변조건

금융 DB와 token registry는 변경되지 않는다.

REQ-001.09-AC01 — Rotation과 무상태성

판정 기준

configured credential 변경은 기존 token을 거부하며 per-token revocation/DB session 구현이 없다.

검증

rotation 테스트와 source/schema/diff 검색을 실행한다.

검증 분류

  • 수준: unit/static
  • 백엔드: pytest와 repository search
  • 케이스: password, signing secret, admin ID rotation

상태 불변조건

검증은 현재 설정과 서명만 사용한다.

REQ-001.10-AC01 — 운영 제약

판정 기준

README와 Kubernetes 문서에 HTTPS, proxy allowlist, rate limit, secret 주입, rotation과 stateless logout이 기록된다.

검증

문서 검색과 deployment manifest 검토를 실행한다.

검증 분류

  • 수준: documentation/static
  • 백엔드: repository search
  • 케이스: README, deploy/k8s/README.md

상태 불변조건

실제 credential·secret 값이 문서와 manifest에 없다.

REQ-001.10-AC02 — 인증 후 기존 페이지 호환성

판정 기준

유효한 Dashboard 인증 cookie로 인증하면 기존 page query parameter를 유지한 채 요약, 자산/마감, 분류 규칙, 확인필요 큐와 월정산 핵심 페이지가 예외 없이 예상 내용을 렌더링한다.

검증

tests/test_dashboard_app.py::test_streamlit_apptest_core_pages_use_synthetic_fixture_db와 tests/test_dashboard_authentication_app.py::test_valid_cookie_allows_dashboard_and_is_not_stored_in_session_state를 실행한다.

검증 분류

  • 수준: regression integration
  • 백엔드: Streamlit AppTest, synthetic SQLite
  • 케이스: 인증된 핵심 page query와 기존 gateway

상태 불변조건

기존 SQLite schema, page query와 gateway contract를 변경하지 않는다.

REQ-001.10-AC03 — MCP 인증 범위 분리

판정 기준

Dashboard cookie 인증은 MCP endpoint의 인증을 의미하지 않으며 MCP server 진입점, transport와 port를 변경하지 않는다.

검증

Base-to-head diff에서 MCP 인증·진입점·transport·port를 담당하는 jarca_ledger_mcp/server.py, jarca_ledger_mcp/adapters/mcp/**, tests/test_mcp_http_runtime.py의 불변성을 확인하고 기존 stdio, streamable HTTP와 invalid transport 회귀 test를 실행한다.

검증 분류

  • 수준: static/regression
  • 백엔드: Git diff, pytest
  • 케이스: MCP source, stdio, streamable HTTP, invalid transport

상태 불변조건

Dashboard와 MCP 인증 경계를 결합하지 않으며 MCP 인증 상태를 변경하지 않는다.

REQ-001.11-AC01 — 안전한 관찰성

판정 기준

허용 event와 correlation ID만 기록되고 민감 값·구체적 검증 원인은 기록되지 않는다.

검증

로그 테스트와 redaction 검색을 실행한다.

검증 분류

  • 수준: unit/static
  • 백엔드: pytest와 repository search
  • 케이스: config, login, cleanup, logout success/failure

상태 불변조건

로그에서 credential·secret·token·cookie가 복원되지 않는다.

8. 오류 및 충돌 계약

동일 요청에서 설정 오류가 credential 오류보다 우선하며, 모든 인증 실패는 보호 자원 전에 종료된다. invalid cookie는 cleanup deletion을 시도하고, cleanup route가 거부되거나 실패해도 보호 자원은 열지 않는다. cross-origin/plain HTTP 요청은 403, malformed request는 400이다. 동시 login은 application lock 없이 독립 처리한다.

9. 데이터 및 인터페이스 계약

Token은 v, sub=dashboard-admin, iat, exp, jti와 서명만 가지며 browser cookie에만 저장된다. 세 환경변수는 server process에서만 읽는다. 상세 HTTP, cookie, cleanup, logout 계약은 docs/interface-contract.md에 정의한다.

10. 하위 호환성

Issue #1의 Dashboard-only 범위, 기존 page/query/gateway/MCP 동작과 구현 PR #3의 추적성을 유지한다. session-only 인증은 persistent cookie로 대체하며 SQLite schema와 MCP transport는 변경하지 않는다.

11. 추적성

  • 근거 이슈: #1, #6
  • 구현 PR: #3, #7
  • 관련 설계 문서: docs/interface-contract.md, docs/project-structure.md, docs/runtime-flow.md, README.md, deploy/k8s/README.md
  • 관련 요구사항: 없음