This file is optimized for AI coding agents (Cursor, Copilot, Claude Code, etc.). It contains everything needed to correctly integrate AuthForge licensing into a project.
AuthForge is a license key validation service. Your app sends a license key + hardware ID to the AuthForge API, gets back a cryptographically signed response, and runs background heartbeats to maintain the session. If the license is revoked or expired, the heartbeat fails and you handle it (typically exit the app).
- 1
login()orvalidate_license()= 1 credit (one/auth/validatedebit each). - 10 heartbeats = 1 credit (billed on every 10th successful heartbeat per license).
- Keep
heartbeat_intervalat>= 10seconds (900/ 15 min is the typical desktop default)./auth/heartbeatis limited to 6 requests/minute per license key, and revocations still take effect on the next heartbeat.
Prefer pip install authforge-sdk from PyPI (installs the cryptography dependency). Imports remain from authforge import …. For a vendored single-file layout, copy authforge.py and add cryptography to your environment. Requires Python 3.9+.
import sys
from typing import Optional
from authforge import AuthForgeClient
def on_failure(reason: str, exc: Optional[Exception]) -> None:
print(f"AuthForge: {reason}", file=sys.stderr)
if exc is not None:
print(exc, file=sys.stderr)
sys.exit(1)
def main() -> None:
client = AuthForgeClient(
app_id="YOUR_APP_ID",
app_secret="YOUR_APP_SECRET",
heartbeat_mode="SERVER",
on_failure=on_failure,
)
license_key = input("Enter license key: ").strip()
if not client.login(license_key):
print("Login failed.", file=sys.stderr)
sys.exit(1)
# --- Your application code starts here ---
print("Running with a valid license.")
# --- Your application code ends here ---
client.logout()
if __name__ == "__main__":
main()| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
app_id |
str |
yes | — | Application ID |
app_secret |
str |
yes | — | Application secret |
heartbeat_mode |
str |
yes | — | "SERVER" or "LOCAL" (case-insensitive) |
heartbeat_interval |
int |
no | 900 |
Seconds between heartbeats (minimum 10) |
api_base_url |
str |
no | https://auth.authforge.cc |
API base URL |
on_failure |
Callable[[str, Optional[Exception]], None] | None |
no | None |
Called on login/heartbeat/network failure; if omitted, process exits via os._exit(1) (not used by validate_license) |
request_timeout |
int |
no | 15 |
HTTP timeout (seconds) |
ttl_seconds |
int | None |
no | None (server default: 86400) |
Requested session token lifetime. Server clamps to [3600, 604800]; preserved across heartbeat refreshes. |
hwid_override |
str | None |
no | None |
Optional custom HWID/subject string. When set to a non-empty value (for example tg:123456789), the SDK sends it instead of generating a machine fingerprint. |
For Telegram/Discord bot flows, prefer immutable IDs (tg:<user_id>, discord:<user_id>) instead of usernames.
| Method | Returns | Description |
|---|---|---|
login(license_key: str) |
bool |
Validates license, verifies signatures, starts heartbeat thread |
validate_license(license_key: str) |
ValidateLicenseResult |
Same validate + signatures as login; no session persistence or heartbeat; never calls on_failure or os._exit |
logout() |
None |
Stops heartbeat and clears session state |
is_authenticated() |
bool |
Whether a session token is present and marked authenticated |
get_session_data() |
dict | None |
Decoded signed payload map |
get_app_variables() |
dict | None |
App-scoped variables |
get_license_variables() |
dict | None |
License-scoped variables |
invalid_app, invalid_key, expired, revoked, hwid_mismatch, no_credits, blocked, rate_limited, replay_detected, session_expired, app_disabled, bad_request
Notes:
replay_detectedis validate-only.rate_limitedcan be returned by/auth/validateand/auth/heartbeat(heartbeat is license-limited at 6/min and has no app-layer IP limit).
vars_map = client.get_license_variables() or {}
tier = vars_map.get("tier")client.logout()Server error codes appear as ValueError in the exc passed to on_failure from failed validation (e.g. invalid_key). Reasons are login_failed, heartbeat_failed, or network_error.
import sys
from typing import Optional
def on_failure(reason: str, exc: Optional[Exception]) -> None:
if isinstance(exc, ValueError) and exc.args:
code = str(exc.args[0])
if code in {"invalid_key", "expired", "revoked"}:
print(f"License issue: {code}", file=sys.stderr)
sys.exit(1)- Do not hardcode the app secret as a plain string literal in source — use environment variables or encrypted config
- Do not skip the
on_failurecallback — without it, heartbeat failures terminate the process viaos._exit(1)without your cleanup - Do not call
login()on every app action — call it once at startup; heartbeats handle the rest - Do not use
heartbeat_mode="LOCAL"unless the app has no internet after initial auth