fix(license): bind deviceId to physical machine, not OS user
Use OS machine identifiers (Windows MachineGuid, macOS IOPlatformUUID, Linux machine-id) hashed as deviceId so all accounts on one PC share one license slot. Keep legacy userData/device.id for migration and retire it on re-activation. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -11,7 +11,7 @@
|
||||
3. **Срок** — поле `exp` (unix секунды) в выданном токене. Клиент отклоняет истёкший токен без сети. На сервере два формата продуктового ключа:
|
||||
- **fixed** — в записи ключа задан `expiresAtSec`; при активации `exp` копируется из него (старый формат).
|
||||
- **period** — задан `validDays`; при **первой** активации лицензии (`sub`) сервер фиксирует `activatedAtSec` и выставляет `exp = activatedAtSec + validDays×86400` для всех устройств. Клиенту тип ключа неизвестен — только итоговый `exp` в токене.
|
||||
4. **Устройства** — поле `did` в токене: при активации сервер привязывает токен к `deviceId` клиента и ведёт учёт списка устройств на `sub` в `data.json` (`maxDevices`).
|
||||
4. **Устройства** — поле `did` в токене: при активации сервер привязывает токен к `deviceId` клиента и ведёт учёт списка устройств на `sub` в `data.json` (`maxDevices`). `deviceId` — **отпечаток физической машины** (не пользователь ОС): Windows `MachineGuid`, macOS `IOPlatformUUID`, Linux `/etc/machine-id`; клиент хеширует значение (SHA-256) и шлёт opaque-строку. На одном ПК разные учётки Windows/macOS/Linux получают один и тот же `deviceId`. Опционально `retireDeviceId` в `POST /v1/activate` снимает старый слот (миграция с per-user UUID).
|
||||
5. **Отзыв** — сервер помечает `sub` в `revokedSubs`. Клиент при наличии `DND_LICENSE_STATUS_URL` запрашивает `GET /v1/status?sub=…`; при `revoked: true` лицензия считается недействительной **без обновления** приложения. Офлайн до истечения `exp` отозванный токен формально криптографически валиден — это осознанный компромисс; при необходимости сокращайте срок жизни токена или добавляйте принудительную онлайн-проверку перед критичными действиями.
|
||||
|
||||
## Продакшен-сборка
|
||||
@@ -20,7 +20,9 @@
|
||||
|
||||
## Хранение на клиенте
|
||||
|
||||
Токен не хранится открытым текстом в JSON userData: используется **Electron `safeStorage`** (на macOS — связка с Keychain, на Windows — DPAPI). Идентификатор устройства — отдельный файл `device.id` (не секрет). Принятие EULA — `preferences.json` (версия текста).
|
||||
Токен не хранится открытым текстом в JSON userData: используется **Electron `safeStorage`** (на macOS — связка с Keychain, на Windows — DPAPI). Идентификатор устройства вычисляется из ID машины ОС (см. выше); файл `device.id` в userData — устаревший per-user UUID, читается только для совместимости до повторной активации. Принятие EULA — `preferences.json` (версия текста).
|
||||
|
||||
Для тестов/отладки можно задать `DND_LICENSE_DEVICE_ID` (готовая строка deviceId, без fingerprint).
|
||||
|
||||
### Linux / WSL без keyring
|
||||
|
||||
|
||||
Reference in New Issue
Block a user