authforge-sdk 1.2.0__tar.gz → 1.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: authforge-sdk
3
- Version: 1.2.0
3
+ Version: 1.2.1
4
4
  Summary: Official Python SDK for AuthForge — credit-based license key authentication with Ed25519-verified responses.
5
5
  Author: AuthForge
6
6
  License: MIT
@@ -110,7 +110,7 @@ client = AuthForgeClient(
110
110
  | Parameter | Type | Default | Description |
111
111
  |---|---|---|---|
112
112
  | `app_id` | str | required | Your application ID from the AuthForge dashboard |
113
- | `app_secret` | str | required | Your application secret from the AuthForge dashboard |
113
+ | `app_secret` | str | required for online APIs; `None` / `""` for `login_from_file` only | Your application secret from the AuthForge dashboard. Do not ship it in air-gapped binaries. |
114
114
  | `public_key` | `str \| Sequence[str]` | required | App Ed25519 public key(s) (base64) from dashboard. Pass one key, a list of keys, or a comma-separated string to trust multiple keys during rotation (see [Key rotation](#key-rotation)). |
115
115
  | `heartbeat_mode` | `str \| None` | `None` | Deprecated shim (see [Migrating from heartbeat_mode](#migrating-from-heartbeat_mode)). Use `online_heartbeat` instead. |
116
116
  | `heartbeat_interval` | int | `900` | Seconds between background checks (minimum `10`; default 15 min). Applies to both grace period checks and online check-ins. |
@@ -159,7 +159,7 @@ Tune `ttl_seconds` to set the grace period duration (server default 24h, clamped
159
159
 
160
160
  ## Offline license files (`.authforge`)
161
161
 
162
- 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 online check-ins.
162
+ 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 online check-ins. Pass `app_secret=None` (or `""`) so the air-gapped binary does not contain the App Secret.
163
163
 
164
164
  | | Grace period (default) | Offline license file |
165
165
  | --- | --- | --- |
@@ -174,7 +174,7 @@ from authforge import AuthForgeClient
174
174
 
175
175
  client = AuthForgeClient(
176
176
  app_id="YOUR_APP_ID",
177
- app_secret="YOUR_APP_SECRET", # unused for offline files but still required by the constructor
177
+ app_secret=None, # login_from_file does not use the App Secret; do not ship it in air-gapped builds
178
178
  public_key="YOUR_PUBLIC_KEY",
179
179
  on_failure=lambda reason, exc: print(reason, exc),
180
180
  )
@@ -82,7 +82,7 @@ client = AuthForgeClient(
82
82
  | Parameter | Type | Default | Description |
83
83
  |---|---|---|---|
84
84
  | `app_id` | str | required | Your application ID from the AuthForge dashboard |
85
- | `app_secret` | str | required | Your application secret from the AuthForge dashboard |
85
+ | `app_secret` | str | required for online APIs; `None` / `""` for `login_from_file` only | Your application secret from the AuthForge dashboard. Do not ship it in air-gapped binaries. |
86
86
  | `public_key` | `str \| Sequence[str]` | required | App Ed25519 public key(s) (base64) from dashboard. Pass one key, a list of keys, or a comma-separated string to trust multiple keys during rotation (see [Key rotation](#key-rotation)). |
87
87
  | `heartbeat_mode` | `str \| None` | `None` | Deprecated shim (see [Migrating from heartbeat_mode](#migrating-from-heartbeat_mode)). Use `online_heartbeat` instead. |
88
88
  | `heartbeat_interval` | int | `900` | Seconds between background checks (minimum `10`; default 15 min). Applies to both grace period checks and online check-ins. |
@@ -131,7 +131,7 @@ Tune `ttl_seconds` to set the grace period duration (server default 24h, clamped
131
131
 
132
132
  ## Offline license files (`.authforge`)
133
133
 
134
- 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 online check-ins.
134
+ 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 online check-ins. Pass `app_secret=None` (or `""`) so the air-gapped binary does not contain the App Secret.
135
135
 
136
136
  | | Grace period (default) | Offline license file |
137
137
  | --- | --- | --- |
@@ -146,7 +146,7 @@ from authforge import AuthForgeClient
146
146
 
147
147
  client = AuthForgeClient(
148
148
  app_id="YOUR_APP_ID",
149
- app_secret="YOUR_APP_SECRET", # unused for offline files but still required by the constructor
149
+ app_secret=None, # login_from_file does not use the App Secret; do not ship it in air-gapped builds
150
150
  public_key="YOUR_PUBLIC_KEY",
151
151
  on_failure=lambda reason, exc: print(reason, exc),
152
152
  )
@@ -360,7 +360,7 @@ class AuthForgeClient:
360
360
  def __init__(
361
361
  self,
362
362
  app_id: str,
363
- app_secret: str,
363
+ app_secret: Optional[str],
364
364
  public_key: PublicKeyArg,
365
365
  heartbeat_mode: Optional[str] = None,
366
366
  heartbeat_interval: int = 900,
@@ -374,8 +374,12 @@ class AuthForgeClient:
374
374
  ) -> None:
375
375
  if not app_id or not isinstance(app_id, str):
376
376
  raise ValueError("app_id must be a non-empty string")
377
- if not app_secret or not isinstance(app_secret, str):
378
- raise ValueError("app_secret must be a non-empty string")
377
+ # Empty/None is valid for offline-only clients (login_from_file).
378
+ # Online APIs (login, validate_license, self_ban) still require a secret.
379
+ if app_secret is None:
380
+ app_secret = ""
381
+ elif not isinstance(app_secret, str):
382
+ raise ValueError("app_secret must be a string or None")
379
383
  public_key_list = self._normalize_public_key_list(public_key)
380
384
  if not public_key_list:
381
385
  raise ValueError(
@@ -558,9 +562,16 @@ class AuthForgeClient:
558
562
  except Exception:
559
563
  pass
560
564
 
565
+ def _require_app_secret(self) -> None:
566
+ if not self.app_secret:
567
+ raise ValueError(
568
+ "app_secret is required for online APIs; omit it only when using login_from_file"
569
+ )
570
+
561
571
  def login(self, license_key: str) -> bool:
562
572
  if not license_key or not isinstance(license_key, str):
563
573
  raise ValueError("license_key must be a non-empty string")
574
+ self._require_app_secret()
564
575
 
565
576
  try:
566
577
  self._validate_and_store(license_key)
@@ -575,6 +586,7 @@ class AuthForgeClient:
575
586
  session state or starting the heartbeat thread."""
576
587
  if not license_key or not isinstance(license_key, str):
577
588
  raise ValueError("license_key must be a non-empty string")
589
+ self._require_app_secret()
578
590
  try:
579
591
  body: Dict[str, Any] = {
580
592
  "appId": self.app_id,
@@ -661,6 +673,7 @@ class AuthForgeClient:
661
673
  resolved_license = explicit_license or current_license
662
674
  if not resolved_license:
663
675
  raise ValueError("missing_license_key")
676
+ self._require_app_secret()
664
677
 
665
678
  body = {
666
679
  "appId": self.app_id,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: authforge-sdk
3
- Version: 1.2.0
3
+ Version: 1.2.1
4
4
  Summary: Official Python SDK for AuthForge — credit-based license key authentication with Ed25519-verified responses.
5
5
  Author: AuthForge
6
6
  License: MIT
@@ -110,7 +110,7 @@ client = AuthForgeClient(
110
110
  | Parameter | Type | Default | Description |
111
111
  |---|---|---|---|
112
112
  | `app_id` | str | required | Your application ID from the AuthForge dashboard |
113
- | `app_secret` | str | required | Your application secret from the AuthForge dashboard |
113
+ | `app_secret` | str | required for online APIs; `None` / `""` for `login_from_file` only | Your application secret from the AuthForge dashboard. Do not ship it in air-gapped binaries. |
114
114
  | `public_key` | `str \| Sequence[str]` | required | App Ed25519 public key(s) (base64) from dashboard. Pass one key, a list of keys, or a comma-separated string to trust multiple keys during rotation (see [Key rotation](#key-rotation)). |
115
115
  | `heartbeat_mode` | `str \| None` | `None` | Deprecated shim (see [Migrating from heartbeat_mode](#migrating-from-heartbeat_mode)). Use `online_heartbeat` instead. |
116
116
  | `heartbeat_interval` | int | `900` | Seconds between background checks (minimum `10`; default 15 min). Applies to both grace period checks and online check-ins. |
@@ -159,7 +159,7 @@ Tune `ttl_seconds` to set the grace period duration (server default 24h, clamped
159
159
 
160
160
  ## Offline license files (`.authforge`)
161
161
 
162
- 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 online check-ins.
162
+ 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 online check-ins. Pass `app_secret=None` (or `""`) so the air-gapped binary does not contain the App Secret.
163
163
 
164
164
  | | Grace period (default) | Offline license file |
165
165
  | --- | --- | --- |
@@ -174,7 +174,7 @@ from authforge import AuthForgeClient
174
174
 
175
175
  client = AuthForgeClient(
176
176
  app_id="YOUR_APP_ID",
177
- app_secret="YOUR_APP_SECRET", # unused for offline files but still required by the constructor
177
+ app_secret=None, # login_from_file does not use the App Secret; do not ship it in air-gapped builds
178
178
  public_key="YOUR_PUBLIC_KEY",
179
179
  on_failure=lambda reason, exc: print(reason, exc),
180
180
  )
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "authforge-sdk"
7
- version = "1.2.0"
7
+ version = "1.2.1"
8
8
  description = "Official Python SDK for AuthForge — credit-based license key authentication with Ed25519-verified responses."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
File without changes
File without changes