Entitlement
핵심 요약
Entitlement(사용권)는 어떤 기능을 사용할 수 있는지 짧은 기간 동안 서명으로 증명합니다. 라이선스가 영구적으로 유효하다는 뜻은 아닙니다.
서버는 실제 라이선스 상태와 기기 증명을 다시 확인하고, 만료·회수된 권한은 거부해야 합니다.
Entitlement는 활성화 시 발행되고 게이트된 모든 호출에 Authorization: Entitlement <jws>로 제시되는 단기 ES256 JWS입니다. TTL 120초(ENTITLEMENT_TTL_SEC); 401에서 클라이언트는 재활성화하고 새 증명으로 한 번 재시도합니다.
클레임 매트릭스
계약 매트릭스 §1에서 인용 — Worker actual = be-cloudflare가 발급/검증; SDK default = @yetdapay/license-server 기본값; preset = createWorkerConfig().
| 클레임 | Worker actual | SDK default | SDK worker 프리셋 |
|---|---|---|---|
iss | "devpay-lic"(verify 시 필수) | "yp" 발급 + 필수 | "devpay-lic" |
aud | 없음 — 발급도 검사도 안 함 | audience 설정이 아니면 없음; 설정 시 발급 + 필수 | 없음(aud 발급은 DEFERRED) |
lic_id | 필수 | 필수 | 동일 |
product_id | 필수; 라이브 라이선스 행과 재검사(드리프트 시 401) | 필수; 저장소와 재검사 | 동일 |
tenant_id | 없음 — DEFERRED | 발급됨(nullable); expectedTenantId를 통해서만 강제 | 없음 허용 |
features | 상품 네임스페이스에서; 없으면 [], 비문자열 드롭 | 필수 문자열 배열 | 동일 |
jkt_bound | 발급 + 필수; 증명 jwk에 바인딩 | 필수(cnf.jkt로 폴백) | 동일 |
cnf.jkt | 발급(jkt_bound와 동일) | 발급 + jkt_bound와 교차 검사 | 동일 |
iat | 발급; Worker에서 검증 안 함(exp만 검사) | 필수; 60초 skew 초과 미래 iat 거부 | 동일 |
exp | iat + 120; 만료 거부 | 필수; exp <= now 거부 | 동일 |
jkt=sha256(sha256(RFC 7638 thumbprint))— DevPay 고유 이중 해시, RFC 9449 아님.products.features_json이 유일한 entitlement 진실 공급원입니다 — 라이선스 행의 기능은 신뢰되지 않습니다.- Worker는 모든 PoP 엔드포인트를
LICENSE_SECURITY_ENABLED뒤에 기능 게이트합니다; 없으면404. - Wire는 DevPay 고유 버전 계약이며 RFC 9449 DPoP가 아닙니다 — 버전 필드나 협상이 없습니다(wire v1; 버전 관리).
aud / tenant_id — 연기됨
Worker는 aud나 tenant_id를 발급하지도 검증하지도 않습니다; 추가하면 토큰 형식이 변경되므로 wire-v2 결정으로 연기됐습니다 (known-limitations §3.8). SDK는 이미 tenant_id(nullable)와 선택적 aud를 발급/정규화합니다.
Worker의 가드 순서
guard()는 순서대로 평가합니다: entitlement 서명/iss/exp → 라이브 라이선스 행 재조회(폐기/만료/상품 불일치) → DPoP sig/htm/htu/iat → jkt 일치 → jti 원자적 소비 (license_nonces) → body 해시 h. 거부는 security_events에 기록됩니다 (detail_json에 키/토큰/PII 없음).
Entitlement가 아닌 것
- 세션 토큰이 아님 — 디바이스 키에 바인딩된 120초 PoP 부여; 라이선스 행 재조회를 통해 비행 중 폐기 가능.
- 자기 완결 권한이 아님 — 서버는 항상 라이브 라이선스 행을 재검사; 유효한 서명만으로는 절대 충분하지 않습니다.