JavaScript / Node 接入指南

在 Node、Electron 中使用 fetch 与 node:crypto 完成激活与 Ed25519 离线验签。

本指南覆盖完整闭环:激活 → 本地保存 → 启动校验 → 停用。示例假设你已经拿到工作区与一份授权码。

1. 激活

license.js
const BASE = 'https://keygate.unionuser.com';

export async function activate(licenseKey, deviceId) {
  const res = await fetch(BASE + '/api/v1/license/activate', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      license_key: licenseKey,
      identifier: deviceId,
      identifier_type: 'device',
    }),
  });
  const json = await res.json();
  if (!json.success) throw new Error(json.error.code);
  return json.data.token;
}

2. 离线验签

verify.js
import { verify, createPublicKey } from 'node:crypto';

const publicKey = createPublicKey({
  key: Buffer.concat([
    Buffer.from('302a300506032b6570032100', 'hex'),
    Buffer.from('bf17435ee7b937b3592808941683c21d37c4e124d95d4e8fdd86fe775dd23b11', 'hex'),
  ]),
  format: 'der',
  type: 'spki',
});

export function verifyToken(token, deviceId) {
  const [payloadB64, sigB64] = token.split('.');
  const ok = verify(
    null,
    Buffer.from(payloadB64, 'ascii'),
    publicKey,
    Buffer.from(sigB64, 'base64url'),
  );
  if (!ok) throw new Error('INVALID_SIGNATURE');

  const claims = JSON.parse(
    Buffer.from(payloadB64, 'base64url').toString('utf8'),
  );
  if (claims.did !== deviceId) throw new Error('DEVICE_MISMATCH');
  if (claims.exp * 1000 < Date.now()) throw new Error('TOKEN_EXPIRED');
  return claims;
}

3. 停用

deactivate.js
export async function deactivate(licenseKey, deviceId) {
  const res = await fetch(BASE + '/api/v1/license/deactivate', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      license_key: licenseKey,
      identifier: deviceId,
    }),
  });
  const json = await res.json();
  if (!json.success) throw new Error(json.error.code);
}

Electron 提示

  • 网络请求放在主进程,渲染进程通过 IPC 调用,避免 token 暴露给页面。
  • token 存在 userData 目录,不要写进安装目录。
  • 启动时先验签再决定是否联网,弱网环境体验更稳。