# Runtime Flow — Dashboard 관리자 인증 ## 목적과 범위 ASGI startup부터 login, token restoration, invalid-cookie cleanup, explicit logout까지의 실행 순서와 상태 계약을 정의한다. - 포함 범위: config, HTTPS routes, cookie verification, Streamlit gate, cleanup/deletion - 제외 범위: MCP request authentication, DB session/token registry, per-token revocation - 시작 조건: trusted ASGI process receives request - 성공 종료 상태: authenticated Dashboard page executes - 실패 종료 상태: matching cookie deletion and unauthenticated login page ## 흐름 ### startup — ASGI startup - 시작 이벤트: process startup - 선행조건: deployment injects configuration without recording values - 실행 방식: synchronous startup/request handling - 트랜잭션·영속성 경계: no auth persistence; existing financial SQLite is untouched | 단계 ID | 실행 주체 | 시작 조건·입력 | 처리 | 읽기·쓰기 상태 | 외부 호출 | 성공 시 다음 단계 | 실패 시 전이 | |---|---|---|---|---|---|---|---| | `startup-01` | Uvicorn | process command | load `dashboard.server:app` | process memory only | none | `startup-02` | `startup-fail` | | `startup-02` | ASGI app | app loaded | mount auth routes and Streamlit runtime | route table only | none | `gate-01` | `startup-fail` | ### login — Credential login - 시작 이벤트: same-origin HTTPS form POST - 선행조건: valid trusted scheme, origin and bounded body - 실행 방식: synchronous request - 트랜잭션·영속성 경계: no DB transaction; cookie is browser state | 단계 ID | 실행 주체 | 시작 조건·입력 | 처리 | 읽기·쓰기 상태 | 외부 호출 | 성공 시 다음 단계 | 실패 시 전이 | |---|---|---|---|---|---|---|---| | `login-01` | auth server | POST `/api/auth/login` | validate scheme/origin/body | request only | none | `login-02` | `login-reject` | | `login-02` | authentication | validated form | load three settings, fail closed | server memory | secret provider/environment | `login-03` | `config-fail` | | `login-03` | authentication | configured settings | constant-time compare ID/password | no persistent write | none | `login-04` | `login-fail` | | `login-04` | authentication | valid credential | issue exactly `v`, `sub=dashboard-admin`, `iat`, `exp`, `jti`; fix `iat`/`exp` at login success for default 24 hours or `remember_me=on` 30 days | server memory | none | `login-05` | `login-fail` | | `login-05` | auth server | signed token | set hardened cookie with selected absolute expiry and redirect; later activity never reissues or extends it | browser cookie write | response | `gate-01` | `login-fail` | ### restore — Existing cookie restoration - 시작 이벤트: new request/session with browser cookie - 선행조건: cookie present or absent; current configuration available - 실행 방식: synchronous gate - 트랜잭션·영속성 경계: no server-side session; financial DB begins only after gate success | 단계 ID | 실행 주체 | 시작 조건·입력 | 처리 | 읽기·쓰기 상태 | 외부 호출 | 성공 시 다음 단계 | 실패 시 전이 | |---|---|---|---|---|---|---|---| | `gate-01` | dashboard app | page startup | call authentication before DB path/page dispatch | no DB access | none | `gate-02` | `unauthenticated` | | `gate-02` | authentication | current config and cookie | verify signature, exact `v`/`sub=dashboard-admin`/`iat`/`exp`/`jti` claims, current admin-ID/password/signing-secret binding and absolute `exp`; never refresh or extend on restoration, rerun or activity | no session or token write | none | `dashboard-01` | `cleanup-01` | | `dashboard-01` | dashboard app | accepted token | resolve DB path and dispatch requested page | existing SQLite reads/writes | gateway/page code | `authenticated` | `dashboard-fail` | ### cleanup — Invalid cookie cleanup - 시작 이벤트: gate rejects malformed, expired, tampered, claim-invalid, or config-failure cookie - 선행조건: same-origin HTTPS cleanup request - 실행 방식: synchronous request - 트랜잭션·영속성 경계: no DB or token registry write | 단계 ID | 실행 주체 | 시작 조건·입력 | 처리 | 읽기·쓰기 상태 | 외부 호출 | 성공 시 다음 단계 | 실패 시 전이 | |---|---|---|---|---|---|---|---| | `cleanup-01` | browser/auth gate | invalid cookie | navigate to `GET /api/auth/cleanup` | request only | auth route | `cleanup-02` | `cleanup-reject` | | `cleanup-02` | auth server | same-origin HTTPS `Referer` | emit matching deletion (`Max-Age=0`) and root redirect | browser cookie deletion | response | `unauthenticated` | `cleanup-reject` | ### logout — Explicit logout - 시작 이벤트: user submits logout form - 선행조건: same-origin HTTPS POST - 실행 방식: synchronous request - 트랜잭션·영속성 경계: no financial DB change and no per-token revocation | 단계 ID | 실행 주체 | 시작 조건·입력 | 처리 | 읽기·쓰기 상태 | 외부 호출 | 성공 시 다음 단계 | 실패 시 전이 | |---|---|---|---|---|---|---|---| | `logout-01` | browser | POST `/api/auth/logout` | validate same-origin HTTPS | request only | none | `logout-02` | `logout-reject` | | `logout-02` | auth server | validated request | matching deletion and root redirect | browser cookie deletion | response | `unauthenticated` | `logout-reject` | ## 실패와 복구 | 대상 단계 | 실패 조건 | 추가 쓰기 중단 | 재시도 계약 | 중복 방지 | 보상·수동 복구 | 최종 상태 | |---|---|---|---|---|---|---| | `login-02` | missing/empty/read-error/short secret | yes | fix deployment config | no cookie | operator injects secret | `config-fail` | | `login-03` | wrong ID/password | yes | retry after generic error and 1s delay | no lock | external rate limit | `login-fail` | | `gate-02` | expired, malformed/tampered claims, or admin-ID/password/signing-secret rotation failure | yes | cleanup then login | matching deletion | user logs in again | `unauthenticated` | | `cleanup-02` | non-HTTPS/cross-origin | yes | retry same origin | deletion is idempotent | browser clears manually if needed | `cleanup-reject` | | `logout-02` | non-HTTPS/cross-origin | yes | retry same origin | deletion is idempotent | browser clears manually if needed | `logout-reject` | ### 동시성과 순서 - 동시 실행 허용 여부: independent requests allowed; no application-wide auth lock - 잠금·직렬화 단위: none for authentication; existing SQLite rules remain for financial writes - 순서 보장: config/cookie gate precedes DB path, gateway and page dispatch - 충돌 검출·처리: token is stateless; password/signing-secret/admin-ID rotation invalidates old claims - 만료 불변조건: login 성공 시 고정한 `iat`와 `exp`는 복원·rerun·활동 동안 바뀌지 않으며 token을 재발급하거나 만료를 연장하지 않음 - rotation 불변조건: admin ID, password 또는 signing secret 변경은 이전 설정으로 발급한 모든 token을 무효화하고 `cleanup-01`로 전이 ## 관찰 가능성 | 신호 종류 | 이름 | 발생 단계·조건 | 상관 식별자 | 진단 속성 | 민감정보 처리 | |---|---|---|---|---|---| | event log | `authentication_required` | `gate-01` rejects | non-sensitive correlation ID | event class only | no cookie/token/reason | | event log | `login_succeeded` | `login-05` | non-sensitive correlation ID | TTL class only | no credential/token | | event log | `login_failed` | `login-03` | non-sensitive correlation ID | generic failure class | no ID/password | | event log | `cleanup` | `cleanup-02` | non-sensitive correlation ID | generic cleanup class | no cookie header | | event log | `logout` | `logout-02` | non-sensitive correlation ID | operation result | no token/cookie value | ## 검증 - 정상 흐름: auth server tests plus loopback HTTPS login and fresh session restoration - 실패 흐름: configuration, wrong credential, malformed/tampered/expired cookie and origin rejection tests - 경계 흐름: TTL boundary, rotation, oversized token, same-host different-port origin - 동시성 흐름: independent failed submissions and no application lock; ingress rate-limit configuration review - 운영 진입점과 기대 결과: `uvicorn dashboard.server:app --host 0.0.0.0 --port 8501`; valid HTTPS reaches login or authenticated page, invalid cookie receives matching deletion before unauthenticated page