Skip to main content

Requirements

  • Rust 1.70 or later
  • Crates used by the SDK:
    • hmac
    • sha2
    • serde
    • serde_json
    • ureq
    • base64
    • mac_address
    • hostname

Installation

The crate is published on crates.io/crates/authforge as authforge.
Or set a semver range in Cargo.toml (for example 1.4 for compatible 1.x releases from 1.4.0, the first with typed check-in errors and on_heartbeat_failure):
Optional: pin branch, tag, or rev.

Quick start

Config struct reference

Validate license (no session)

validate_license runs the same /auth/validate request and signature checks as login, but does not store a session on the client or start the background thread. It returns the same LoginResult on success.

Billing

  • Each successful login() or validate_license() costs 1 credit (one /auth/validate debit).
  • The grace period is free: local session re-verification makes no network calls and consumes no credits.
  • Online check-ins (if enabled) cost 1 credit per 10 successful heartbeats (billed on every 10th call).
  • With online check-ins, revocations take effect on the next check-in regardless of interval; without them, at session expiry.
  • A new self-serve offline activation (activate_offline) costs 2 credits; activating the same machine again is free.

Methods reference

Error enum reference

err.code() returns the machine-readable code for any variant, err.to_string() its Display form, and err.is_transient() / err.is_fatal() its classification (1.4.0+). AuthForgeError implements std::error::Error and is #[non_exhaustive] (2.0.0+): a match on it needs a _ => arm, and later minor releases may add variants. The Rust SDK never exits the process: login / validate_license return Result, and background failures go to your callback. Request retries are automatic: rate_limited (or a 429 without an error code) is retried after 2s, then 5s; a 429 with no_credits, demo_quota_exceeded, or app_burn_cap_reached is not retried and waits for the next interval; a network failure is retried once after 2s.

Background check failures

Each failed grace period check or online check-in is reported once, to on_heartbeat_failure (or, if that isn’t set, to on_failure as the error’s Display string). Without a callback (1.4.1+), a transient failure prints AuthForge: background check failed (<code>); retrying next interval to stderr and checks again next interval; a fatal one (including SessionExpired) clears the session and stops background checks without output, and the process keeps running, so check is_authenticated() or set a callback. The reason is err.code(), and err.is_transient() / err.is_fatal() tell you what the SDK did about it: Local session expiry (the grace period ending, or a transient failure after the TTL) is reported as AuthForgeError::SessionExpired (code() is "session_expired", definitive), like the other SDKs (2.0.0+; 1.x reported Expired). Expired means the server returned expired. On check-ins, hwid_mismatch means this HWID is no longer bound to the license (for example after an HWID reset in the dashboard), and blocked means the HWID or IP is blacklisted, or isn’t on the app’s whitelist. A failed check-in only counts as a server verdict when its body is a JSON object with "status": "failed" and a non-empty error; any other failure body is the transient unexpected_response. authforge::is_transient_error_code(code) applies the same classification to a code string. To tolerate short outages but shut down on a definitive answer, have the callback signal your main thread and let it save and exit:
Calling std::process::exit(1) inside the callback is a last resort: no destructors run on any thread, so save the user’s work first. Thread safety: callbacks run on the heartbeat thread with no SDK lock held. Calling logout() or is_authenticated() from a callback, or dropping the client there, is safe. A check-in still in flight when logout() or a new login() runs never writes its result to the new session.

Grace period and online check-ins

By default, the app runs through the grace period after activation: the SDK re-verifies the signed session locally, makes no network calls, and stops when the session expires. Opt in to online check-ins for fast revocation and concurrent-use detection:
See Online Check-ins for a detailed comparison.

Offline license files (.authforge)

For machines that never connect to the internet, the operator mints a signed offline license file in the dashboard or Developer API (1 credit). The SDK verifies it locally with your public key and the machine HWID; no network, no check-ins, and online login is untouched. Leave app_secret empty (Default::default() already does) so the air-gapped binary does not contain the App Secret.
Errors are the OfflineLicenseError enum (separate from AuthForgeError), in check order: BadArmor, BadSignature, UnsupportedVersion, MalformedPayload, WrongApp, Expired, HwidMismatch, plus ReadError(String) for an unreadable path; .code() gives the cross-SDK string and on_failure receives offline_login_failed: <code>. client.verify_license_file(path_or_text) and the crate function authforge::verify_license_file(text, &opts) run the same checks without touching state. Issued files cannot be revoked remotely; they stay valid until their own expiry. create_activation_request(opts) produces the .authforge-request the operator uploads; hostname is omitted unless include_machine_name is set. See Activation requests.

Migrating from heartbeat_mode

The heartbeat_mode field and the HeartbeatMode enum are deprecated but still accepted:
  • heartbeat_mode: HeartbeatMode::Local: remove the field; the grace period is now the default.
  • heartbeat_mode: HeartbeatMode::Server: use online_heartbeat: true instead.
Do not import HeartbeatMode in new code.

Self-serve offline activation

client.activate_offline(license_key, Some(path)) -> Result<(), AuthForgeError> turns a license key into a lifetime .authforge file bound to this machine, with one online call to POST /auth/offline/activate. After that the app starts with login_from_file and never needs the network. Use it for one-time-purchase apps that should run offline forever. See Self-serve activation and the walkthrough Sell offline apps.
  • Needs the App Secret. Set app_secret in AuthForgeConfig (air-gapped builds leave it empty).
  • Your app must opt in (off by default), and the license must be perpetual and seat-limited.
  • The SDK sends the same HWID as login, verifies the returned file (signature, app id, this machine’s HWID) before saving it, writes it to the path atomically (creating parent folders), and leaves the client in an offline session exactly as after login_from_file: variables available, no grace-period timer, no check-ins.
  • Billing: 2 credits per new machine (one validation plus one mint), charged to you. The same machine activating again gets the identical file back for free. Every refusal is free.
  • A license can activate at most maxHwidSlots distinct machines, ever. HWID resets do not free machine slots.
  • A refund or chargeback revokes the license online but cannot reach files already issued. For a lifetime file, that means the buyer keeps the app.
Failures are AuthForgeError values. The new codes arrive as AuthForgeError::Other(..), so match on err.code(); err.is_fatal() / err.is_transient() give the classification. The codes are the server’s (offline_activation_disabled, offline_activation_requires_perpetual, offline_activation_requires_seats, offline_activation_limit_reached, hwid_mismatch when no seat is free, invalid_key, revoked, no_credits, rate_limited, …) or the SDK’s: offline_file_rejected (fatal; the server’s file failed local verification, reason in the message) and file_write_failed (transient; the file could not be written, and calling again returns the same file for free). The method never calls on_failure and never exits the process; nothing is written on failure and any existing session is left alone. On offline_activation_limit_reached, tell the user to contact you. Messages to show: Self-serve activation errors. login_from_file can report a missing or rejected file to on_failure as offline_login_failed. If you set a callback, return normally for that reason so the app reaches the key prompt.