All posts

How to add licence keys with an HWID lock to a Python app

Protect a Python program with licence keys, a one-PC lock and a live kill switch using the free pwfauth package from PyPI: install it, sign in, start the heartbeat, read the licence and move a key to a new PC

3 min read

Python makes it easy to ship a tool, and just as easy for a paid copy to end up in a group chat. The pwfauth package from PyPI adds licence keys to your program in a few lines: the key locks to one PC, and when you ban it in your panel, the program closes within 30 seconds. This guide takes you from an empty panel to a protected script

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. Your program needs it to talk to PWF Auth

Step 2: install the package

pip install pwfauth

It needs Python 3.9 or newer and one dependency, cryptography, because Python has no AES in its standard library. HTTP comes from the standard library. Use version 1.3.0 or newer: the server signs every reply, and older versions can't check the current signature

Step 3: sign in and start the heartbeat

Read the app secret from an environment variable, so it never sits in your source:

import os
import sys
from pwfauth import PwfClient

client = PwfClient(os.environ["PWFAUTH_SECRET"])

def on_session_ended(error_code, message):
    # banned, paused, expired, reset, ended from the panel, or no server for 90 seconds
    print(f"Session ended ({error_code}): {message}", file=sys.stderr)
    os._exit(1)

client.on_session_ended = on_session_ended

login = client.login(key)
if not login.success:
    sys.exit(login.message)   # written for the customer

client.start_heartbeat()   # the kill switch: never skip it

start_heartbeat() is what turns a one-time check into a live session. Ban the key in your panel and on_session_ended fires on the customer's PC on the next check-in, without waiting for the next launch. A console tool or a service can call client.run_heartbeat() instead, which blocks until the session ends

on_session_ended runs on the heartbeat's own thread, so sys.exit would only end that thread: use os._exit, or hand the work to your GUI's thread

In a GUI app

Pass the event to your toolkit's thread. With Tkinter:

client.on_session_ended = lambda code, msg: root.after(0, sign_out, code, msg)

Step 4: read the licence

lic = login.license
if lic.is_lifetime:
    print("Lifetime licence")
else:
    print(f"Expires {lic.expires_at:%Y-%m-%d}, {lic.days_remaining} day(s) left")

A lifetime key has no expiry date. Treating it as a date is the most common mistake with licence APIs, and is_lifetime is there so you never have to

Step 5: move a licence to a new PC

A key is bound to the first PC that signs in with it. When a customer changes PC, offer to move it:

from pwfauth import PwfErrorCodes

login = client.login(key)
if login.error_code in (PwfErrorCodes.HWID_MISMATCH, PwfErrorCodes.DEVICE_LIMIT):
    if ask_user("This key is used on another computer. Move it here?"):
        moved = client.reset_hardware_id(key, "New laptop")
        if moved.success:
            login = client.login(key)
        else:
            show(moved.message)   # for example the waiting time between two moves

You decide in the panel whether customers may move keys themselves, and how long they wait between two moves

Customer accounts

Prefer a username and a password? The same package handles accounts, with the same session and the same kill switch:

client.register_account("bob", "S3cret!pass", email="bob@example.com")
client.account_login("bob", "S3cret!pass")
client.redeem_key(another_key)   # adds a key's time to the account

More you can do

  • client.check_key(key): read a key's state without opening a session or using a PC seat
  • client.check_update("1.4.2"): is there a newer build to download?
  • client.create_trial(): a free trial key for this PC, when trials are on
  • client.set_variable(name, value): values kept on the server for each customer

The hardware ID is made the same way as in the .NET and Node.js packages, so one PC counts as one device whatever language your apps use

The package on GitHub

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