테마
target_session_token PoP (same-device merge)
같은 user-agent 에 두 logi 계정 세션이 모두 활성일 때 OTP 없이 merge 하는 PoP 자격증명. cross-device 케이스는 OTP 사용.
흐름
- Target(B) 세션에서 토큰 발급 → raw 문자열 1회 노출 (5분 TTL).
- 같은 브라우저 frontend 가 토큰을 Requester(A) 세션으로 전달 (postMessage/sessionStorage 등 자유).
- Requester(A) 가
/me/merge에target_session_token제출 → atomic single-use 소비. - Audit:
identity_links.merged_via = "session_token".
API
토큰 발급
http
POST /api/v1/me/merge/session-token
Authorization: <target 세션 쿠키 또는 PAK>
Content-Type: application/json
{ "idempotency_key": "uuid-optional" }응답 (200):
json
{
"session_token": "ltgt_8f3a...c91",
"expires_at": "2026-05-11T13:25:00Z"
}- 평문 토큰은 응답에 1회 노출. 서버는 SHA-256 hash 만 저장.
- TTL: 발급 시점 +5분.
- 같은
idempotency_key재호출 시 같은 미사용 토큰 반환.
토큰 사용 (merge)
http
POST /api/v1/me/merge
Authorization: <requester 세션 쿠키 또는 PAK>
Content-Type: application/json
# Option A — cross-device OTP
{ "target_user_id": 42, "otp_code": "123456" }
# Option B — same-device session_token
{ "target_session_token": "ltgt_8f3a...c91" }응답 (200):
json
{
"ok": true,
"identity_link_id": 1287,
"primary_user_id": 17,
"linked_user_id": 42,
"merged_via": "session_token"
}오류 코드
| 상태 | 코드 | 의미 |
|---|---|---|
| 422 | conflicting_credentials | otp_code 와 target_session_token 동시 전송 |
| 422 | missing_credentials | 둘 다 비어있음 |
| 422 | self_merge_forbidden | 토큰 target user_id == requester user_id |
| 401 | token_expired | 5분 TTL 초과 |
| 401 | token_consumed | 이미 사용된 토큰 |
| 401 | invalid_token | hash 불일치 또는 미존재 |
보안 모델
| 항목 | 동작 |
|---|---|
| 저장 | SHA-256 hash 만 merge_session_tokens.token_digest 에 저장 |
| 단일 사용 | atomic consumed_at set, 두 번째 시도는 token_consumed |
| TTL | 발급 +5분 |
| Target binding | 토큰은 발급 세션의 user_id 에 묶임 |
| Self-merge 차단 | requester == target 이면 422 self_merge_forbidden |
| Audit | identity_links.merged_via = "session_token" |
OTP vs session_token
| OTP | session_token | |
|---|---|---|
| 사용 시점 | cross-device | same-device |
| PoP 매체 | verified 이메일 6자리 | target 세션 쿠키 + hash 토큰 |
| TTL | 10분 | 5분 |
| 재사용 | 1회 후 폐기 | atomic single-use |
PAK 로 발급 (서버-서버 / CI-CD)
요구사항
- PAK 가
account:merge스코프 보유. - 요청 본문에
via: "pak"명시 (스코프만으로 자동 승격 안 됨). issued_via = "pak"가 레코드 + audit log 양쪽에 기록.
curl
bash
curl -X POST https://api.1pass.dev/api/v1/me/merge/session-token \
-H "Authorization: Bearer logi_pak_xxxxxxxx_yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy" \
-H "Content-Type: application/json" \
-d '{"via":"pak","idempotency_key":"ci-merge-2026-05-11-001"}'오류
- 스코프 없이
via=pak→403 insufficient_scope({"error":"insufficient_scope","required":"account:merge"}). - 알 수 없는
via값 →400 invalid_via. account:mergePAK 분실 시 즉시 revoke.
Limitations
- iframe / cross-origin: SameSite 정책으로 target 세션 쿠키 미전송 가능 → 같은 top-level origin 에서 발급/전달 또는 OTP 폴백.
- cross-device 의도 명확 시 OTP 권장.