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
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 pwfauthIt 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 itstart_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_endedruns on the heartbeat's own thread, sosys.exitwould only end that thread: useos._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 movesYou 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 accountMore you can do
client.check_key(key): read a key's state without opening a session or using a PC seatclient.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 onclient.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


