All posts

How to add licence keys with an HWID lock to a Node.js app

Protect an Electron app, a CLI tool or any Node.js program with licence keys, a one-PC lock and a live kill switch using the free pwfauth package from npm: zero dependencies, TypeScript types included

3 min read

Node.js runs a lot of paid software that never sees a browser: Electron desktop apps, command-line tools, automation scripts and bots. The pwfauth package from npm gives them licence keys, a lock to one PC and a kill switch in a few lines. It has no dependencies at all, it's built on Node's own fetch and crypto, and it ships TypeScript definitions

Server side only. The package holds your app secret, so it belongs in the Node.js side of your program (Electron's main process, a CLI, a service), never in code that runs in a web page

Step 1: create the application

Sign in to your panel, open Applications and press New application. Give it a name: the app secret is made for you

Step 2: install the package

npm install pwfauth

It needs Node 18 or newer

Step 3: sign in and start the heartbeat

Read the app secret from an environment variable, or from your build, never from a file you publish:

import { PwfClient } from 'pwfauth';

const client = new PwfClient(process.env.PWFAUTH_SECRET);

// banned, paused, expired, reset, ended from the panel, or no server for 90 seconds
client.on('sessionEnded', ({ errorCode, message }) => {
  console.error(`Session ended (${errorCode}): ${message}`);
  process.exit(1);
});

let login = await client.login(key);
if (!login.success) {
  console.error(login.message);   // written for the customer
  process.exit(1);
}

client.startHeartbeat();   // the kill switch: never skip it

startHeartbeat() is the part that enforces your licence. Every 30 seconds it checks the key again, and a ban, a pause, an expiry or a reset in your panel ends the session on the next check-in. Only an encrypted reply counts as an answer: three check-ins in a row without one end the session with NETWORK_LOST, so blocking the server in a firewall or moving the clock doesn't keep a banned copy running. The timer never holds a process open on its own

Step 4: read the licence

const lic = login.license;
if (lic.isLifetime) {
  console.log('Lifetime licence');
} else {
  console.log(`Expires ${lic.expiresAt.toISOString()}, ${lic.daysRemaining} day(s) left`);
}

expiresAt and daysRemaining are null for a lifetime key, which is the most common mistake with licence APIs, so isLifetime is there to check first

Step 5: move a licence to a new PC

import { PwfErrorCodes } from 'pwfauth';

if (login.errorCode === PwfErrorCodes.HWID_MISMATCH || login.errorCode === PwfErrorCodes.DEVICE_LIMIT) {
  const moved = await client.resetHardwareId(key, 'New laptop');
  if (moved.success) login = await client.login(key);
}

You decide in the panel whether customers may move keys themselves, and how long they wait between two moves. The hardware ID is the same one the .NET, Python and C++ clients use, so one PC counts as one device whatever language your apps are written in

More you can do

await client.checkKey(key);                 // a key's state, without a session or a PC seat
await client.checkUpdate('1.4.2');          // is there a newer build? version, sha256, download link
await client.createTrial();                 // a free trial for this PC, when trials are on
await client.accountLogin('alice', 's3cret'); // customer accounts instead of keys
await client.redeemKey(anotherKey);         // adds a key's time to the account

A note on versions

The Node.js package checks that every reply is encrypted with your app secret. The extra check of PWF Auth's own server signature, which the C# and VB.NET, Python and C++ clients already have, isn't in the Node.js package yet. Keep it up to date with npm update pwfauth

The package on npm

Protect your app for free

Licence keys, customer accounts and the kill switch, in every language. Make an account and sign in with your first key in a few minutes

Create a free account