Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.authforge.cc/llms.txt

Use this file to discover all available pages before exploring further.

Requirements

  • Python 3.9 or later
  • Dependencies: cryptography and typing_extensions (installed automatically with the PyPI package).

Installation

Install from PyPI as authforge-sdk. In code, import the authforge module:
pip install authforge-sdk
Need a vendored single file? Copy authforge.py from the GitHub repository and ensure cryptography is declared in your environment. The published wheel is recommended for most projects.

Quick start

from authforge import AuthForgeClient

client = AuthForgeClient(
    app_id="YOUR_APP_ID",
    app_secret="YOUR_APP_SECRET",
    public_key="YOUR_PUBLIC_KEY",
    heartbeat_mode="SERVER",
)

license_key = input("Enter license key: ")

if client.login(license_key):
    print("Authenticated!")
    # Your app logic here; heartbeats run in the background
else:
    print("Invalid license key.")
    exit(1)

Constructor parameters

client = AuthForgeClient(
    app_id="...",               # Required; from dashboard
    app_secret="...",         # Required; from dashboard
    public_key="YOUR_PUBLIC_KEY",  # Required; Ed25519 public key from your AuthForge dashboard (base64)
    heartbeat_mode="SERVER",  # Required; "SERVER" or "LOCAL"
    heartbeat_interval=900,   # Optional; seconds (default: 900, any value ≥ 1 is supported)
    api_base_url="https://auth.authforge.cc",  # Optional
    on_failure=None,          # Optional; callable(reason: str, exc: BaseException | None)
    request_timeout=15,       # Optional; HTTP timeout in seconds
    ttl_seconds=None,         # Optional; requested session TTL. None = server default (24h). Clamped to [3600, 604800].
    hwid_override=None        # Optional; custom identity (for example "tg:123456789")
)

ttl_seconds

Requested session token lifetime in seconds for /auth/validate. Pass None (or omit) to accept the server default of 24 hours. The server clamps to [3600, 604800] (1 hour to 7 days). The requested TTL is preserved across heartbeat refreshes so long-running apps in LOCAL mode can extend their offline window up to 7 days.

Billing

  • Each successful login() or validate_license() costs 1 credit (one /auth/validate debit).
  • Heartbeats cost 1 credit per 10 successful calls (billed on every 10th heartbeat). Any heartbeat_interval ≥ 1 is economically safe.
  • Revocations take effect on the next heartbeat regardless of interval.

Login

success = client.login(license_key)
Returns True if authentication succeeded, False otherwise. On success, the SDK starts a background heartbeat thread automatically.

Validate license (no heartbeat)

result = client.validate_license(license_key)
if result["valid"]:
    print(result["session_token"], result["app_variables"], result["license_variables"])
else:
    print(result["code"])
Same /auth/validate flow and signatures as login, without storing session state on the client or starting the heartbeat thread. On success, the result includes optional entitlement convenience fields when the server sends them: session_expires_at, license_expires_at (None for lifetime keys after a JSON null), max_hwid_slots, hwid_count, and license_label. The full signed payload remains in session_data.

Failure callback

If authentication or a heartbeat fails, the SDK calls your on_failure callback. If no callback is set (or the callback raises an exception), the SDK calls os._exit(1).
def handle_failure(reason: str, exception: Exception | None):
    if reason == "login_failed":
        print("Login failed; check your license key.")
    elif reason == "heartbeat_failed":
        print("Heartbeat failed; saving state and shutting down.")
        save_application_state()
    # The SDK will exit after this callback returns if you don't handle it

client = AuthForgeClient(
    app_id="YOUR_APP_ID",
    app_secret="YOUR_APP_SECRET",
    public_key="YOUR_PUBLIC_KEY",
    heartbeat_mode="SERVER",
    on_failure=handle_failure,
)
If you don’t set on_failure, the SDK terminates the process immediately on any failure. Always set a callback in production to handle graceful shutdown.

Reading variables

After a successful login, app variables and license variables are available on the client:
if client.login(license_key):
    # App-wide variables (set in dashboard or API)
    app_vars = client.get_app_variables() or {}
    if app_vars.get("maintenanceMode"):
        print("Server is under maintenance.")
        exit(0)

    # Per-license variables
    license_vars = client.get_license_variables() or {}
    plan = license_vars.get("plan", "basic")
    if plan == "pro":
        enable_pro_features()

Heartbeat modes

# SERVER mode; checks with the API every interval
client = AuthForgeClient(
    app_id="...",
    app_secret="...",
    public_key="YOUR_PUBLIC_KEY",
    heartbeat_mode="SERVER",
    heartbeat_interval=900,  # 15 minutes
)

# LOCAL mode; verifies locally, re-validates when the session token expires (default 24h; up to 7d via ttl_seconds)
client = AuthForgeClient(
    app_id="...",
    app_secret="...",
    public_key="YOUR_PUBLIC_KEY",
    heartbeat_mode="LOCAL",
    heartbeat_interval=900,
)
See Heartbeat Modes for a detailed comparison.

Full example

import signal
import sys
from authforge import AuthForgeClient

def on_auth_failure(reason, exception):
    print(f"Auth failure: {reason}")
    if exception:
        print(f"  Detail: {exception}")
    save_state()
    sys.exit(1)

def save_state():
    print("Saving application state...")
    # Your save logic here

client = AuthForgeClient(
    app_id="YOUR_APP_ID",
    app_secret="YOUR_APP_SECRET",
    public_key="YOUR_PUBLIC_KEY",
    heartbeat_mode="SERVER",
    heartbeat_interval=900,
    on_failure=on_auth_failure,
)

license_key = input("Enter license key: ")
if not client.login(license_key):
    print("Invalid license key.")
    sys.exit(1)

print("Licensed and running!")

# Check variables
app_vars = client.get_app_variables() or {}
min_version = app_vars.get("minVersion")
if min_version and __version__ < min_version:
    print(f"Please update to version {min_version} or later.")
    sys.exit(1)

# Your application main loop
try:
    while True:
        # App logic here
        pass
except KeyboardInterrupt:
    save_state()

GitHub

Full source, changelog, and issues: AuthForgeCC/authforge-python