Skip to content

About

Official Rust SDK for AuthForge — license key authentication with HMAC-verified heartbeats

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

AuthForge Rust SDK

Official Rust SDK for AuthForge: online license activation, Ed25519 signed sessions with a grace period, HWID binding, and optional online check-ins.

How it works

  1. Activate: login() calls POST /auth/validate online. The server checks revocation, expiry, HWID binding, and credits, then returns an Ed25519-signed session with a TTL.
  2. Grace period (the default): the app keeps running on the signed session without contacting AuthForge. The grace period equals the session TTL: the server default is 24h, and requested values are clamped to 1h to 7d. When it expires, the SDK clears the session and reports SessionExpired to on_heartbeat_failure (or on_failure).
  3. Online check-ins (opt-in): set online_heartbeat: true to send periodic POST /auth/heartbeat requests instead. This gives you fast revocation and concurrent-use detection at the cost of network traffic and heartbeat credits.

Features

Everything in this list ships in src/lib.rs today:

  • Online activation via POST /auth/validate, returning a signed LoginResult.
  • Ed25519 signature verification on every /auth/validate and /auth/heartbeat response; tampered or unsigned responses are rejected. verify_payload_signature_ed25519[_any] is exported for standalone use.
  • Key rotation: configure public_key (single key or comma-separated string) and/or public_keys (rotation set). The SDK trusts a signature that matches any configured key, so you can roll the server-side signing key without breaking deployed clients.
  • Nonce anti-replay: a fresh nonce is sent on every request and the echoed nonce in the signed payload is checked; a mismatch returns ReplayDetected.
  • HWID: derived from the OS machine id (macOS IOPlatformUUID, Linux /etc/machine-id, Windows MachineGuid), identical across all AuthForge SDKs for the same app and machine. See Hardware ID.
  • hwid_override: bind to any identity instead of the machine (for example tg:<id>, discord:<id>).
  • Seat enforcement: the server binds each HWID into a license's free slots up to max_hwid_slots; hwid_count / max_hwid_slots are surfaced on LoginResult. A shared (unlimited-seat) key skips per-device binding.
  • Grace period by default, opt-in online check-ins (see Grace period and online check-ins).
  • Offline license files (.authforge): login_from_file / verify_license_file verify a cloud-minted, Ed25519-signed file with zero network access for air-gapped machines.
  • Self-ban (self_ban(...)) for anti-tamper response, both pre-session and post-session.
  • Grace period duration control (session_ttl_seconds) with server-side clamping to [3600, 604800] (1h to 7d).
  • App variables / license variables for feature flags and tiered licensing.
  • Automatic retries for rate-limited and transient network failures, with a fresh nonce per retry.
  • Returns Result instead of exiting: unlike the Python/Node/C#/C++ SDKs, the Rust SDK never exits the process; login/validate_license return Result and background failures are reported through on_heartbeat_failure (typed) or on_failure (see Background check failures).

Installation

The crate is published on crates.io/crates/authforge as authforge.

From your project root:

cargo add authforge

Or declare a semver range in Cargo.toml (for example 2.0 picks up 2.0.x patches):

[dependencies]
authforge = "2.0"

Git dependency (unreleased changes)

To track the GitHub repo instead of a crates.io release:

[dependencies]
authforge = { git = "https://github.com/AuthForgeCC/authforge-rust" }

Optional: pin a branch or revision with branch = "main" or rev = "...".

Path dependency (vendored / local checkout)

Clone or submodule this repo and point at the crate directory:

[dependencies]
authforge = { path = "../authforge-rust" }

Adjust the path to match your layout (for example vendor/authforge-rust).

Quick start

Activate online once at startup; the app then runs through the grace period with no further network calls:

use authforge::{AuthForgeClient, AuthForgeConfig, AuthForgeError};
use std::sync::mpsc;

fn main() {
    let (license_lost, lost) = mpsc::channel::<AuthForgeError>();
    let client = AuthForgeClient::new(AuthForgeConfig {
        app_id: "your-app-id".into(),
        app_secret: "your-app-secret".into(),
        public_key: "your-public-key".into(),
        on_heartbeat_failure: Some(Box::new(move |err: &AuthForgeError| {
            if err.is_fatal() {
                // Runs on the heartbeat thread: signal main instead of exiting here.
                let _ = license_lost.send(err.clone());
            }
        })),
        ..Default::default()
    });

    match client.login("XXXX-XXXX-XXXX-XXXX") {
        Ok(result) => println!("Activated! Grace period seconds remaining: {}", result.expires_in),
        Err(e) => {
            eprintln!("Activation failed: {:?}", e);
            std::process::exit(1);
        }
    }

    let err = lost.recv().expect("client dropped"); // your app's work runs elsewhere until this fires
    eprintln!("Auth failed: {}", err.code());
    // Save the user's work here, then exit.
    std::process::exit(1);
}

To enable online check-ins instead, add online_heartbeat: true (and optionally tune heartbeat_interval).

Config options

Field Type Default Description
app_id String required Application ID from the dashboard
app_secret String empty for login_from_file only Application secret from the dashboard. Required for login / validate_license / activate_offline. Leave empty in air-gapped builds.
public_key String required* App Ed25519 public key (base64) from dashboard. Accepts a comma-separated trust list. *Required unless public_keys is set.
public_keys Vec<String> optional Rotation set of trusted keys. Merged ahead of public_key; the SDK trusts a signature matching any entry (see Key rotation).
online_heartbeat bool false true enables online check-ins (periodic /auth/heartbeat). false (default) runs through the grace period with no network calls after activation.
heartbeat_mode HeartbeatMode Local Deprecated: see Migrating from HeartbeatMode.
heartbeat_interval u64 900 Seconds between online check-ins or grace period checks (minimum 10; default 15 min)
api_base_url String https://auth.authforge.cc API base URL
on_failure Option<Box<dyn Fn(&str)+Send+Sync>> None Callback invoked when auth fails. Background check failures arrive as the error's Display form ("revoked", "network_error: ...") unless on_heartbeat_failure is set
on_heartbeat_failure Option<Box<dyn Fn(&AuthForgeError)+Send+Sync>> None Receives background check failures as the typed error, instead of on_failure (see Background check failures)
request_timeout u64 15 Request timeout in seconds
heartbeat_request_timeout Option<u64> None (8 seconds) HTTP timeout in seconds for /auth/heartbeat only. Keep it below request_timeout so a stuck check-in doesn't hold up logout()
session_ttl_seconds Option<u64> None (server default: 86400) Requested grace period duration in seconds. Server clamps to [3600, 604800] (1h to 7d); preserved across check-in refreshes.
hwid_override Option<String> None Optional custom hardware/subject identifier. When set to Some(non-empty), the SDK sends it verbatim instead of deriving the HWID from the OS machine id.

Identity-based binding example (Telegram/Discord)

let client = AuthForgeClient::new(AuthForgeConfig {
    app_id: "YOUR_APP_ID".into(),
    app_secret: "YOUR_APP_SECRET".into(),
    public_key: "YOUR_PUBLIC_KEY".into(),
    online_heartbeat: true,
    hwid_override: Some(format!("tg:{telegram_user_id}")), // or format!("discord:{discord_user_id}")
    ..Default::default()
});

Key rotation

To rotate the server-side signing key without a flag-day, configure both the new and previous keys; the SDK accepts a signature matching any entry:

let client = AuthForgeClient::new(AuthForgeConfig {
    app_id: "YOUR_APP_ID".into(),
    app_secret: "YOUR_APP_SECRET".into(),
    public_keys: vec!["NEW_PUBLIC_KEY".into(), "PREVIOUS_PUBLIC_KEY".into()],
    ..Default::default()
});

A comma-separated public_key ("NEW,PREVIOUS") works too, for env-var convenience.

Grace period and online check-ins

  • Grace period (the default, no config needed): after a successful online activation, the app keeps running on the Ed25519-signed session without contacting AuthForge. On each heartbeat_interval the SDK confirms the session is still authenticated and that the stored expiry has not passed, failing with SessionExpired once it has. (The signature was already verified at activation; the check is expiry-only and does not re-verify the cached signature.) The grace period equals the session TTL: server default 24h, clamped to 1h to 7d via session_ttl_seconds.
  • Online check-ins (online_heartbeat: true): the SDK sends /auth/heartbeat on each interval, verifies the signature + nonce, and refreshes the stored session. Use this when you need fast revocation or concurrent-use detection; revocations take effect on the next check-in rather than at the end of the grace period.

Either way, the grace period is session continuation after one successful online activation, not persistent offline licensing. The app must reach AuthForge again once the signed session expires. For machines that can never reach AuthForge, use an offline license file instead.

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, 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:

Classification Codes What the SDK does
Definitive (is_fatal()) revoked, expired, hwid_mismatch, blocked, session_expired, malformed_request, app_disabled, invalid_app, signature_mismatch (listed in authforge::DEFINITIVE_ERROR_CODES, which also holds the activate_offline codes offline_activation_disabled, offline_activation_requires_perpetual, offline_activation_requires_seats, offline_activation_limit_reached and offline_file_rejected) Clears the session (as logout() does) and stops background checks, then invokes the callback. is_authenticated() is already false inside it.
Transient (is_transient()) Everything else: network_error, timeout, rate_limited, system_error, no_credits, demo_quota_exceeded, app_burn_cap_reached, bad_request, invalid_key, every http_error_N, invalid_json_response, unexpected_response, and any code this SDK version doesn't know Keeps the session, invokes the callback, and checks again at the next interval. A transient failure after the session TTL has passed is reported as SessionExpired instead (definitive).

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:

use authforge::{AuthForgeClient, AuthForgeConfig, AuthForgeError};
use std::sync::mpsc;

let (license_lost, lost) = mpsc::channel::<AuthForgeError>();

let client = AuthForgeClient::new(AuthForgeConfig {
    app_id: "YOUR_APP_ID".into(),
    app_secret: "YOUR_APP_SECRET".into(),
    public_key: "YOUR_PUBLIC_KEY".into(),
    online_heartbeat: true,
    on_heartbeat_failure: Some(Box::new(move |err: &AuthForgeError| {
        if err.is_transient() {
            eprintln!("authforge: check-in failed ({}), retrying next interval", err.code());
            return;
        }
        eprintln!("authforge: license no longer valid: {}", err.code());
        // Runs on the heartbeat thread: signal the main thread instead of exiting here.
        let _ = license_lost.send(err.clone());
    })),
    ..Default::default()
});

// ... login ...

while lost.try_recv().is_err() {
    do_one_unit_of_work(); // keep units short so the loop notices the signal quickly
}
save_user_work();
client.logout();
std::process::exit(1);

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: session state is only read and written under the client's internal lock, and 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.

Offline license files (.authforge)

For machines that never connect to the internet, the operator mints a signed offline license file in the AuthForge dashboard (License page -> Mint .authforge file) or via POST /v1/licenses/{licenseKey}/offline-files. The file is a standalone Ed25519-signed document; the SDK verifies it with only your app public key and the machine HWID. It never contacts AuthForge and never starts the background thread. Leave app_secret empty so the air-gapped binary does not contain the App Secret.

Grace period (default) Offline license file
Needs network Once, at login() Never on the end machine
What is verified Signed session from /auth/validate Signed document minted in the cloud
Lifetime Session TTL: 1h to 7d Operator-chosen expiry or lifetime (perpetual licenses only)
Revocation Picked up at the next online validate / check-in Not reachable: the file stays valid until its own expiry
Cost 1 credit per login() 1 credit per mint; verifying is free
use authforge::{AuthForgeClient, AuthForgeConfig, OfflineLicenseError};

let client = AuthForgeClient::new(AuthForgeConfig {
    app_id: "YOUR_APP_ID".into(),
    public_key: "YOUR_PUBLIC_KEY".into(),
    on_failure: Some(Box::new(|msg| eprintln!("authforge: {msg}"))),
    ..Default::default()
});

// 1. Write an activation request the operator drops into the mint dialog:
std::fs::write("machine.authforge-request", client.create_activation_request(Default::default())).expect("write request");

// 2. Later, authorize from the minted file (path or armored text). No network.
match client.login_from_file("license.authforge") {
    Ok(lic) => println!("Offline license OK until {:?}", lic.expires_at), // None = lifetime
    Err(OfflineLicenseError::Expired) => eprintln!("offline license expired - ask the operator for a new file"),
    Err(OfflineLicenseError::HwidMismatch) => eprintln!("this file is bound to a different machine"),
    Err(err) => eprintln!("offline license rejected: {err}"),
}

Since 2.0 every AuthForge SDK derives the same HWID for the same app and machine, so the HWID can come from any SDK build of the same app (the exception is the legacy fallback on machines without an OS machine id; see Hardware ID). HWIDs collected before 2.0 no longer match. After login_from_file, get_session_kind() returns Some(SessionKind::Offline) (Some(SessionKind::Online) after login, None when logged out).

authforge::verify_license_file(text, &opts) (crate function) and client.verify_license_file(path_or_text) perform the same checks without touching client state. Errors are the OfflineLicenseError enum, in check order: BadArmor, BadSignature, UnsupportedVersion, MalformedPayload, WrongApp, Expired, HwidMismatch (plus ReadError when a path cannot be read); .code() gives the cross-SDK string. login_from_file also reports offline_login_failed: <code> through on_failure. These are separate from the AuthForgeError enum used by the online APIs.

File format (version 1): PEM-style armor with informational headers, a base64 JSON payload (v, appId, licenseKey, jti, kid, issuedAt, expiresAt, hwid policy, optional label/variable snapshots) and a detached Ed25519 signature over the UTF-8 bytes of the base64 payload string - the same contract as /auth/validate. See offline_license_vectors.json for conformance vectors.

Self-serve offline activation

For apps sold with a lifetime license that should then run without the network: the app turns the buyer's license key into a lifetime, HWID-bound .authforge file with one online call, activate_offline(license_key, path) (POST /auth/offline/activate). From then on login_from_file succeeds with no network calls. This is separate from air-gapped offline files: those are minted by the operator and the air-gapped build ships with no app secret, while self-serve activation is an online call and needs the app secret.

The app must opt in (app settings -> self-serve offline activation), and the license must be perpetual (no expiry) and seat-limited (not unlimited HWIDs). Each license can activate at most max_hwid_slots distinct machines, ever.

use std::path::Path;
use authforge::{AuthForgeClient, AuthForgeConfig};

let client = AuthForgeClient::new(AuthForgeConfig {
    app_id: "YOUR_APP_ID".into(),
    app_secret: "YOUR_APP_SECRET".into(), // required for activate_offline
    public_key: "YOUR_PUBLIC_KEY".into(),
    ..Default::default()
});
let license_path = Path::new("license.authforge");

let ready = license_path.exists() && client.login_from_file("license.authforge").is_ok();
if !ready {
    // No file yet, or it failed (e.g. HwidMismatch after a hardware change): ask for the key.
    let key = prompt_for_key();
    match client.activate_offline(&key, Some(license_path)) {
        Ok(()) => {} // file written and verified; the client is in an offline session
        Err(err) => {
            match err.code() {
                "offline_activation_limit_reached" => eprintln!("Every machine on this license is used. Please contact the vendor."),
                "offline_activation_disabled" => eprintln!("This app does not offer offline activation."),
                "offline_activation_requires_perpetual" | "offline_activation_requires_seats" => {
                    eprintln!("This license cannot be activated offline. Please contact the vendor.")
                }
                "hwid_mismatch" => eprintln!("No free seat on this license for this machine."),
                "invalid_key" | "revoked" | "expired" => eprintln!("That license key is not valid."),
                code if err.is_transient() => eprintln!("Could not reach AuthForge ({code}); try again."),
                code => eprintln!("Activation failed: {code}"),
            }
            std::process::exit(1);
        }
    }
}
// Run the app. No network calls, no background thread.

What activate_offline does:

  • Fails fast without a request: InvalidKey for an empty key, then InvalidApp when the app secret (or app id / public key) is missing.
  • Posts appId, appSecret, licenseKey, the same HWID login() sends, and a fresh nonce, with the same retry policy as login().
  • Verifies the returned file with your public key(s), app id and this machine's HWID before writing anything. A file that fails is refused with offline_file_rejected: <reason> (bad_signature, wrong_app, hwid_mismatch, ...).
  • With Some(path), writes the file byte-for-byte and atomically (temp file + rename), creating parent directories. A failed write is file_write_failed (transient). With None, nothing is written.
  • On success the client is authenticated exactly as after login_from_file (SessionKind::Offline, variables, get_offline_license(), no background thread). On any error, nothing is written, an existing file at path is untouched, the session state is unchanged, and on_failure is not called.
Code Meaning Class
offline_activation_disabled The app has not opted in to self-serve activation fatal
offline_activation_requires_perpetual The license has an expiry fatal
offline_activation_requires_seats The license has unlimited HWID slots fatal
offline_activation_limit_reached Every machine slot on the license is used. HWID resets do not free them; the buyer must contact the vendor fatal
offline_file_rejected The returned file failed local verification (SDK code; reason in the message) fatal
hwid_mismatch No free seat for this machine fatal

The new codes arrive as AuthForgeError::Other(code); match on err.code(). Existing codes (invalid_key, revoked, no_credits, rate_limited, ...) keep their meaning and classification.

Billing and limits:

  • A new machine costs 2 credits, charged to the app owner once. A refused activation costs nothing.
  • The same machine activating again is free: the server returns the identical file (lost file, reinstall, double click).
  • Refunds, chargebacks and revokes cannot reach issued files. They revoke the license online, but a lifetime file already on a machine keeps working.

Hardware ID

The HWID (v2, since 2.0) is

SHA256("authforge-hwid-v2|" + app_id + "|" + machine_id) as lowercase hex,

where machine_id is the operating system's own machine id, trimmed and lowercased:

OS Source
macOS IOPlatformUUID (/usr/sbin/ioreg -rd1 -c IOPlatformExpertDevice)
Linux /etc/machine-id, then /var/lib/dbus/machine-id
Windows HKLM\SOFTWARE\Microsoft\Cryptography\MachineGuid (64-bit registry view, via reg.exe /reg:64)

These ids survive network changes, renames, disk usage and USB devices. Including the app id means two different apps on the same machine get unrelated HWIDs. Every AuthForge SDK computes the same value for the same app id and machine; the shared hwid_vectors.json pins this (alongside offline_license_vectors.json, activation_request_vectors.json and self_serve_activation_vectors.json).

If no valid OS id can be read (for example a container without /etc/machine-id), the SDK falls back to legacy:<the pre-2.0 fingerprint of hostname, OS and MAC> as the machine id. Containers built from one image can also share a baked-in machine id, so for containers prefer hwid_override with an identifier you control.

For non-device identities (for example Telegram users), pass hwid_override such as tg:<user_id>.

Migrating from HeartbeatMode

AuthForgeConfig.heartbeat_mode and the HeartbeatMode enum are deprecated. The old field still works but emits deprecation warnings. If both fields are set, either one enables online check-ins: HeartbeatMode::Server is not overridden by online_heartbeat: false.

  • heartbeat_mode: HeartbeatMode::Local maps to the default behavior (the grace period). Just remove the field:
// Before
AuthForgeConfig { heartbeat_mode: HeartbeatMode::Local, ..Default::default() }
// After
AuthForgeConfig { ..Default::default() }
  • heartbeat_mode: HeartbeatMode::Server maps to online check-ins:
// Before
AuthForgeConfig { heartbeat_mode: HeartbeatMode::Server, ..Default::default() }
// After
AuthForgeConfig { online_heartbeat: true, ..Default::default() }

If both are set, online_heartbeat: true or HeartbeatMode::Server (either one) enables online check-ins.

Billing

  • 1 login() or validate_license() call = 1 credit (one /auth/validate debit each).
  • 10 online check-ins on the same license = 1 credit (billed every 10th successful check-in). The grace period makes no network calls and costs nothing after activation.
  • 1 new offline activation (activate_offline) = 2 credits; re-activating the same machine is free.

With online check-ins, a desktop app running 6h/day at a 15-minute interval burns roughly 3 to 4 credits/day. /auth/heartbeat is limited to 6 requests/minute per license key, so keep intervals at 10 seconds or higher and choose cadence based on revocation speed needs (revocations always land on the next check-in).

Methods

  • login(&self, license_key: &str) -> Result<LoginResult, AuthForgeError>
  • validate_license(&self, license_key: &str) -> Result<LoginResult, AuthForgeError>: same /auth/validate + verification as login, without storing session or starting the background thread; on_failure is not called for network errors on this path
  • self_ban(&self, license_key: Option<&str>, session_token: Option<&str>, revoke_license: bool, blacklist_hwid: bool, blacklist_ip: bool) -> Result<(), AuthForgeError>
  • login_from_file(&self, path_or_text: &str) -> Result<OfflineLicense, OfflineLicenseError>: authorizes from an offline .authforge file with no network; never starts the background thread; failures are echoed to on_failure as offline_login_failed: <code>
  • activate_offline(&self, license_key: &str, path: Option<&Path>) -> Result<(), AuthForgeError>: self-serve offline activation. One online call returns a lifetime .authforge file for this machine; it is verified, written to path and starts an offline session. Needs the app secret
  • verify_license_file(&self, path_or_text: &str) -> Result<OfflineLicense, OfflineLicenseError>: same checks without changing state
  • get_offline_license(&self) -> Option<OfflineLicense>: the offline file in use (jti, expires_at, hwid_policy, …)
  • get_session_kind(&self) -> Option<SessionKind>: Some(SessionKind::Online), Some(SessionKind::Offline), or None when logged out
  • hwid(&self) -> &str: the HWID this client sends (or hwid_override); customers share it to receive a bound file
  • create_activation_request(&self, opts) -> String: unsigned .authforge-request for this machine. No network, no secret. Hostname omitted unless include_machine_name
  • logout(&self)
  • is_authenticated(&self) -> bool
  • get_session_data(&self) -> Option<serde_json::Value>
  • get_app_variables(&self) -> Option<std::collections::HashMap<String, serde_json::Value>>
  • get_license_variables(&self) -> Option<std::collections::HashMap<String, serde_json::Value>>

Error handling

Errors are returned as AuthForgeError, including:

  • InvalidApp
  • InvalidKey
  • Expired (the server's expired: the license has expired)
  • Revoked
  • HwidMismatch
  • NoCredits
  • AppBurnCapReached
  • Blocked
  • RateLimited
  • ReplayDetected
  • AppDisabled
  • SessionExpired (the server's session_expired, and also local session TTL expiry)
  • RevokeRequiresSession
  • BadRequest (bad_request)
  • MalformedRequest (malformed_request)
  • SystemError
  • SignatureMismatch
  • NetworkError(String) (code() is network_error, timeout or invalid_json_response)
  • Other(String) (the offline_activation_* codes, unknown server codes, and SDK codes such as http_error_502, unexpected_response, offline_file_rejected or file_write_failed)

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 (see Background check failures). AuthForgeError implements std::error::Error. It is #[non_exhaustive], so a match on it needs a _ => arm; later minor releases may add variants for new server codes.

Retry behavior is handled inside the internal HTTP request layer:

  • rate_limited, or a 429 without an error code: retry after 2s, then 5s (max 3 attempts total). A 429 with no_credits, demo_quota_exceeded or app_burn_cap_reached is not retried
  • network transport failure: retry once after 2s
  • retries regenerate a fresh nonce when request payload includes nonce

Self-ban (tamper response)

Use self_ban(...) when anti-tamper checks trigger:

// Post-session (authenticated): revoke + HWID/IP blacklist.
client.self_ban(None, None, true, true, true)?;

// Pre-session: provide a license key; SDK forces revoke off client-side.
client.self_ban(Some("AF-XXXX-XXXX-XXXX"), None, true, true, true)?;

// Explicit flags:
client.self_ban(None, None, false, true, true)?;

self_ban(...) chooses request mode automatically:

  • Uses post-session mode when a session token is available (session_token arg or current SDK session).
  • Falls back to pre-session mode with license_key + nonce + app secret.
  • In pre-session mode, revoke is always disabled client-side to avoid unsafe key revocations.
  • Not available after login_from_file: offline sessions have no server session, so self_ban with no explicit license_key / session_token returns Err(AuthForgeError::Other("offline_session")) without contacting the server.

License

MIT

About

Official Rust SDK for AuthForge — license key authentication with HMAC-verified heartbeats

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages