"로그인 됐다" 는 쿠키에 뭐가 들었느냐에 따라 뜻이 완전히 달라져. 지배적인 패턴이 셋인데, 각각 실패하는 방식이 달라.
| 패턴 | 쿠키 안 내용 | 서버 저장 | Revocation |
|---|---|---|---|
| Session 쿠키 | 랜덤 opaque 토큰 (예: 32 hex 문자) | DB 에 token → user_id, expires_at, ip 매핑 | row 삭제 — 즉시 |
| JWT (signed, stateless) | self-describing JSON + signature | 없음 | 만료 전엔 제대로 revoke 못 함. denylist 를 따로 둬야 함 |
| Bearer 토큰 (API key) | Authorization 헤더의 랜덤 문자열 | DB row + scope | row 삭제 |
솔로 앱한테 session 이 이기는 이유
이 퀘스트의 PIN layer 는 SQLite 테이블의 session 쿠키 써. 이유:
- 즉시 revocation. "Revoke All" 이 SQL
DELETE한 줄. JWT 는 stateless 의 핵심을 망가뜨리는 denylist 필요. - IP 에 묶여. session row 마다 발급된 IP 를 같이 저장해. IP 가 안 맞으면 그 쿠키는 무효야. 공짜로 얻는 anti-replay 지.
- admin 에서 보임. active session 리스트, 발급 시간, 발급 위치, 만료 시간 다 봐.
- 작아. 바깥 의존성이 없어. SQLite 는 Python 에 들어있거든.
중요한 쿠키 플래그
| 플래그 | 효과 | 기본 입장 |
|---|---|---|
HttpOnly | JavaScript 가 쿠키 못 읽음 | 항상 ON — XSS 절도 차단 |
Secure | HTTPS 로만 전송 | 프로덕션은 ON, localhost 에서만 OFF |
SameSite=Strict | cross-site 요청에 안 보냄 | 솔로 앱은 ON — cross-site flow 필요 없음 |
Max-Age | 수명 (초) | 제한을 둬. 예를 들면 30일. "무제한" 은 절대 안 돼 |