주소가 500자였던 것은 곁가지고, 진짜 문제는 그 auto= 가 빌더 액세스 토큰 통짜(sub 에 UserInfo 전체 — role 포함)였다는 것이다. 메일 전달 한 번이 그날 자정까지의 권한 양도였고, 브라우저 히스토리·프록시 로그·Referer 에도 남았다. SNS 승인 흐름에서는 같은 이유로 "기존 액세스 토큰을 승인 링크에 얹지 않는다" 를 원칙으로 박아 뒀는데 이 경로에만 남아 있었다. - 0023: place_posts.edit_token_hash. 승인 토큰과 같은 규약(평문은 메일에만) - GET /v1/site/post/edit?t=<코드> → 검증 후 day-pass 를 그 자리에서 만들어 /blog?...#auto=<JWT> 로 303. ★ 프래그먼트는 서버 로그·Referer 에 안 남는다 - 프론트는 hash 에서 읽고 **주소창에서 지운다**. 쿼리도 계속 받는다 — 이미 나간 메일이 자정까지 살아 있고 그걸 깨면 그 링크들이 통째로 죽는다 - 두 코드는 서로 다른 칸에 산다. 하나로 둘 다 되면 일회성이 무의미해진다 - blog_service.app_origin() 으로 오리진 계산을 옮겼다 — 라우터도 같은 값을 쓴다 링크 길이 약 500자 → 약 75자. test_blog_owner 45 passed (신규 6건: 길이·프래그먼트·소유자·만료·없는 코드· 승인 코드 교차 사용). test_upcoming_only_returns_next_week_in_date_order 1건은 기준(stash)에서도 동일하게 실패하는 기존 건. npm run lint 통과 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
18 lines
1.3 KiB
SQL
18 lines
1.3 KiB
SQL
-- 0023 · place_posts.edit_token_hash — 메일의 "고쳐서 올리려면" 링크를 일회용 코드로.
|
|
--
|
|
-- ★ 예전에는 그 링크에 **빌더 액세스 토큰 통짜**(sub 에 UserInfo 전체 — role 포함)를
|
|
-- 쿼리로 실어 보냈다. 주소가 500자였던 것은 그 때문이고, 길이보다 나쁜 것은 따로 있다:
|
|
-- 메일을 한 번 전달하면 **그날 자정까지 빌더 권한이 그대로 넘어간다.** 브라우저 히스토리 ·
|
|
-- 앞단 프록시 로그 · Referer 에도 그대로 남는다.
|
|
-- (SNS 승인 흐름에서는 같은 이유로 "기존 액세스 토큰을 승인 링크에 얹지 않는다" 를
|
|
-- 설계 원칙으로 박아 뒀는데, 이 경로에만 남아 있었다.)
|
|
--
|
|
-- ★ 승인 토큰(approve_token_hash)과 같은 규약이다 — 평문은 메일 본문에만 있고 DB 에는
|
|
-- sha256 만 둔다. 만료는 token_expires_at 을 같이 쓴다(두 링크가 같은 시각에 죽는다).
|
|
ALTER TABLE public.place_posts ADD COLUMN IF NOT EXISTS edit_token_hash varchar(64);
|
|
|
|
-- 링크 한 번에 한 행을 집는다. 승인 토큰과 같은 이유로 부분 인덱스다.
|
|
CREATE INDEX IF NOT EXISTS ix_place_posts_edit_token
|
|
ON public.place_posts(edit_token_hash)
|
|
WHERE deleted = false AND edit_token_hash IS NOT NULL;
|