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:
Ivan Fontosh
2026-07-23 10:39:46 +08:00
parent c1c332364c
commit 32a5479086
11 changed files with 399 additions and 24 deletions
@@ -55,6 +55,30 @@ void test('verifyLicenseToken: неверное устройство', () => {
assert.equal(bad.reason, 'wrong_device');
});
void test('verifyLicenseToken: принимает legacy deviceId при миграции', () => {
const { publicKey, privateKey } = generateKeyPairSync('ed25519');
const pubB64 = publicKey.export({ type: 'spki', format: 'der' }).toString('base64');
const payload = {
v: 1 as const,
sub: 'lic_legacy',
pid: 'dnd_player',
iat: 100,
exp: 2_000_000_000,
did: 'legacy-uuid-from-userdata',
};
const body = canonicalJson(payload);
const sig = sign(null, Buffer.from(body, 'utf8'), privateKey);
const token = joinSignedLicenseToken(body, new Uint8Array(sig.buffer, sig.byteOffset, sig.byteLength));
const ok = verifyLicenseToken(token, {
nowSec: 1_700_000_000,
deviceId: 'machine-fingerprint-hash',
alsoAcceptDeviceIds: ['legacy-uuid-from-userdata'],
publicKeyOverrideSpkiDerB64: pubB64,
});
if (!ok.ok) assert.fail(`expected ok, got ${ok.reason}`);
});
void test('verifyLicenseToken: токен с переносами строк после копирования', () => {
const { publicKey, privateKey } = generateKeyPairSync('ed25519');
const pubB64 = publicKey.export({ type: 'spki', format: 'der' }).toString('base64');