Skip to main content

Requirements

  • C++17 or later
  • libsodium: Ed25519 signature verification
  • OpenSSL: SHA-256 and helpers
  • libcurl: HTTPS requests

Installation

There is no central C++ package registry. Consume the official SDK from GitHub; AuthForgeCC/authforge-cpp (use a release tag under Releases).

Option A: FetchContent (CMake)

Pin a tag (for example v1.4.0) and link the authforge_sdk target:
1.4.0 changes the AuthForgeClient class layout. When upgrading, rebuild everything that includes authforge_sdk.h.

Option B: Install prefix + find_package

Build and install the SDK, then point CMake at the prefix:
In your application:

Dependencies

Install development packages for libsodium, OpenSSL, and libcurl before configuring CMake. Examples:
  • Linux (Debian/Ubuntu): sudo apt install libsodium-dev libssl-dev libcurl4-openssl-dev
  • macOS (Homebrew): brew install libsodium openssl curl; set CMAKE_PREFIX_PATH if CMake does not find Homebrew prefixes.
  • Windows: Use vcpkg for libsodium, openssl, and curl, then pass -DCMAKE_TOOLCHAIN_FILE=.../vcpkg.cmake.

Quick start

Constructor parameters

ttlSeconds (grace period)

Requested session token lifetime in seconds for /auth/validate, passed as the 9th constructor parameter. This is the grace period: how long the app keeps running on the signed session without contacting AuthForge. Pass 0 (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, and apps relying on the grace period alone can extend their window up to 7 days.

Billing

  • Each successful Login() or ValidateLicense() 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.

Login

Returns true on success, false otherwise. On success, a background thread re-verifies the signed session locally during the grace period (and sends online check-ins if OnlineHeartbeat::On was passed).

Validate license (no session)

Same /auth/validate request and Ed25519 verification as Login, without persisting session fields on the client or starting the background thread. Does not invoke the failure callback or std::exit on error; inspect valid / errorCode instead.

Failure callback

If authentication fails, an online check-in fails, or the session expires, the SDK calls your onFailure callback. Returning normally from the callback keeps the process running; if the callback throws, the SDK calls std::exit(1). Without a callback (1.4.1+), a transient background check failure writes AuthForge: background check failed (<code>); retrying next interval to std::cerr and check-ins continue; every other failure (a rejected Login, a definitive check-in answer, the grace period running out) calls std::exit(1).

Typed errors and check-in failures (1.4.0+)

Login(), SelfBan(), and background checks pass an authforge::AuthForgeError as ex (reach it with dynamic_cast from the const std::exception*), with code(), isTransient(), and isFatal(). For server codes ex->what() equals the code. A non-2xx response with a JSON body yields the server’s clean code on both login and check-ins (what() is invalid_key, not http_error_401: {...}; this also applies to ValidateLicenseResult::errorCode); only non-JSON error pages become http_error_<status>. authforge::IsTransientErrorCode(code) exposes the same classification.
  • A transient failure after the session’s signed TTL has passed is reported as session_expired (definitive). The grace period check also ends with session_expired.
  • On check-ins, hwid_mismatch means this device’s HWID is no longer bound to the license (for example after an HWID reset), and blocked means the HWID or IP is blacklisted or not on the app’s whitelist.
  • unexpected_response means a check-in failure body that is not {"status":"failed","error":"<code>"} (for example a proxy error in JSON). It is never treated as a verdict; what() keeps the raw status/error.
  • Unknown snake_case server codes are passed through as-is instead of becoming unknown_error.
Request retries are automatic: rate_limited (or HTTP 429 without an error code) is retried after 2s, then 5s; no_credits, demo_quota_exceeded, and app_burn_cap_reached also use HTTP 429 but are not retried, and the next check-in happens at the next interval; a network failure is retried once after 2s, then fails with network_error or timeout (what() starts with url_error: ). Thread safety. Background onFailure calls run on the heartbeat thread with no SDK lock held. Calling Logout(), IsAuthenticated(), or Login() from the callback, or destroying the client inside it, is safe. Logout() stops check-ins without blocking; the destructor stops the heartbeat thread and joins it (waiting for an in-flight check-in, if any). To tolerate short outages but shut down on a definitive answer, have the callback signal your main thread and let the main thread save and exit:
In a GUI app, post a message to the UI thread instead (for example PostMessage on Windows or QMetaObject::invokeMethod in Qt) and close the main window normally. Calling std::exit(1) inside onFailure is a last resort: it skips the destructors of objects on every thread’s stack, so save the user’s work first.
If you don’t set an onFailure callback, a rejected login or a definitive background failure (including session_expired once the grace period runs out) terminates the process immediately via std::exit(1), without unwinding the stack. Transient check-in failures only print a warning to std::cerr. Always set a callback in production.

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 by passing authforge::OnlineHeartbeat::On as the 4th constructor argument:
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. Pass an empty appSecret so the air-gapped binary does not contain the App Secret.
Failures call onFailure("offline_login_failed", &exc) with exc.what() set to the code and return false (never std::exit). Codes, in check order: bad_armor, bad_signature, unsupported_version, malformed_payload, wrong_app, expired, hwid_mismatch. client.VerifyLicenseFile(pathOrText) and the free function authforge::VerifyLicenseFile(text, appId, publicKeys, hwid) run the same checks without touching state and return a VerifyLicenseFileResult. Issued files cannot be revoked remotely; they stay valid until their own expiry. CreateActivationRequest() produces the .authforge-request the operator uploads; hostname is omitted unless includeMachineName is set. See Activation requests.

Migrating from "SERVER" / "LOCAL"

The legacy constructors taking a "SERVER" or "LOCAL" string as the 4th argument are deprecated but still work:
  • "LOCAL": drop the argument; the grace period is now the default.
  • "SERVER": pass authforge::OnlineHeartbeat::On instead.

Full example (game)

Platform notes

GitHub

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