shieldauth-key 0.1.0__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.
@@ -0,0 +1,37 @@
1
+ Metadata-Version: 2.4
2
+ Name: shieldauth-key
3
+ Version: 0.1.0
4
+ Summary: License key protection for Python apps — one line of code
5
+ License: MIT
6
+ Project-URL: Homepage, https://shieldauth.pages.dev
7
+ Project-URL: Source, https://github.com/shieldauth/shieldauth-python
8
+ Requires-Python: >=3.8
9
+ Description-Content-Type: text/markdown
10
+ Requires-Dist: requests>=2.28
11
+
12
+ # shieldauth
13
+
14
+ License key protection for Python apps — one line of code.
15
+
16
+ ## Install
17
+
18
+ ```bash
19
+ pip install shieldauth
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ```python
25
+ import shieldauth
26
+ shieldauth.verify("sk_live_xxxx")
27
+
28
+ # Your app code here — only runs if license is valid
29
+ print("Welcome!")
30
+ ```
31
+
32
+ On first run, the user is prompted for their license key. It's saved locally so they're never asked again.
33
+
34
+ ## Links
35
+
36
+ - Dashboard: https://shieldauth.pages.dev
37
+ - Docs: https://shieldauth.pages.dev/docs
@@ -0,0 +1,26 @@
1
+ # shieldauth
2
+
3
+ License key protection for Python apps — one line of code.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install shieldauth
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```python
14
+ import shieldauth
15
+ shieldauth.verify("sk_live_xxxx")
16
+
17
+ # Your app code here — only runs if license is valid
18
+ print("Welcome!")
19
+ ```
20
+
21
+ On first run, the user is prompted for their license key. It's saved locally so they're never asked again.
22
+
23
+ ## Links
24
+
25
+ - Dashboard: https://shieldauth.pages.dev
26
+ - Docs: https://shieldauth.pages.dev/docs
@@ -0,0 +1,20 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "shieldauth-key"
7
+ version = "0.1.0"
8
+ description = "License key protection for Python apps — one line of code"
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = { text = "MIT" }
12
+ dependencies = ["requests>=2.28"]
13
+
14
+ [project.urls]
15
+ Homepage = "https://shieldauth.pages.dev"
16
+ Source = "https://github.com/shieldauth/shieldauth-python"
17
+
18
+ [tool.setuptools.packages.find]
19
+ where = ["."]
20
+ include = ["shieldauth*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,4 @@
1
+ from ._verify import verify
2
+
3
+ __all__ = ["verify"]
4
+ __version__ = "0.1.0"
@@ -0,0 +1,8 @@
1
+ import hashlib
2
+ import socket
3
+ import uuid
4
+
5
+
6
+ def get() -> str:
7
+ raw = f"{uuid.getnode()}:{socket.gethostname()}"
8
+ return hashlib.sha256(raw.encode()).hexdigest()[:32]
@@ -0,0 +1,37 @@
1
+ def ask_for_key(app_name: str) -> str:
2
+ """Prompt the user for a license key.
3
+ Uses a tkinter GUI dialog if available (works inside compiled EXEs),
4
+ falls back to terminal input.
5
+ """
6
+ try:
7
+ import tkinter as tk
8
+ from tkinter import simpledialog
9
+
10
+ root = tk.Tk()
11
+ root.withdraw()
12
+ root.lift()
13
+ root.attributes("-topmost", True)
14
+
15
+ key = simpledialog.askstring(
16
+ title=f"{app_name} — License",
17
+ prompt=f"Enter your {app_name} license key:",
18
+ parent=root,
19
+ )
20
+ root.destroy()
21
+
22
+ if key is None:
23
+ # User closed the dialog
24
+ print("License key required. Exiting.")
25
+ raise SystemExit(1)
26
+
27
+ return key.strip()
28
+
29
+ except ImportError:
30
+ # tkinter not available — use terminal
31
+ pass
32
+
33
+ key = input(f"Enter your {app_name} license key: ").strip()
34
+ if not key:
35
+ print("License key required. Exiting.")
36
+ raise SystemExit(1)
37
+ return key
@@ -0,0 +1,42 @@
1
+ import json
2
+ import os
3
+ from pathlib import Path
4
+ from typing import Optional
5
+
6
+
7
+ def _path() -> Path:
8
+ return Path.home() / ".shieldauth" / "keys.json"
9
+
10
+
11
+ def load(app_secret: str) -> Optional[dict]:
12
+ p = _path()
13
+ if not p.exists():
14
+ return None
15
+ try:
16
+ data = json.loads(p.read_text())
17
+ return data.get(app_secret)
18
+ except Exception:
19
+ return None
20
+
21
+
22
+ def save(app_secret: str, entry: dict) -> None:
23
+ p = _path()
24
+ p.parent.mkdir(parents=True, exist_ok=True)
25
+ try:
26
+ data = json.loads(p.read_text()) if p.exists() else {}
27
+ except Exception:
28
+ data = {}
29
+ data[app_secret] = entry
30
+ p.write_text(json.dumps(data, indent=2))
31
+
32
+
33
+ def clear(app_secret: str) -> None:
34
+ p = _path()
35
+ if not p.exists():
36
+ return
37
+ try:
38
+ data = json.loads(p.read_text())
39
+ data.pop(app_secret, None)
40
+ p.write_text(json.dumps(data, indent=2))
41
+ except Exception:
42
+ pass
@@ -0,0 +1,123 @@
1
+ from datetime import datetime, timezone, timedelta
2
+ from typing import Optional
3
+
4
+ import requests
5
+
6
+ from . import _hwid, _store, _prompt
7
+
8
+ API_BASE = "https://shieldauth.pages.dev/api/v1"
9
+ OFFLINE_GRACE_DAYS = 7
10
+
11
+
12
+ def _post(endpoint: str, payload: dict, timeout: int = 8) -> Optional[dict]:
13
+ try:
14
+ r = requests.post(f"{API_BASE}/{endpoint}", json=payload, timeout=timeout)
15
+ return r.json()
16
+ except Exception:
17
+ return None
18
+
19
+
20
+ def verify(app_secret: str) -> None:
21
+ hwid = _hwid.get()
22
+ stored = _store.load(app_secret)
23
+
24
+ # ── Case 1: No saved key — need to prompt ────────────────────────────────
25
+ if not stored:
26
+ # Fetch app_name first (key=None)
27
+ res = _post("validate", {"app_secret": app_secret, "hwid": hwid})
28
+ if res is None:
29
+ print("Unable to reach ShieldAuth servers and no cached key found. Exiting.")
30
+ raise SystemExit(1)
31
+
32
+ app_name = res.get("app_name", "ShieldAuth")
33
+
34
+ if "error" in res and res["error"] == "invalid_app_secret":
35
+ # Developer misconfigured — show a generic error
36
+ print("Invalid application configuration. Exiting.")
37
+ raise SystemExit(1)
38
+
39
+ key = _prompt.ask_for_key(app_name)
40
+ _activate(app_secret, key, hwid, app_name)
41
+ return
42
+
43
+ # ── Case 2: Saved key — validate silently ────────────────────────────────
44
+ key = stored["key"]
45
+ app_name = stored.get("app_name", "ShieldAuth")
46
+
47
+ res = _post("validate", {"app_secret": app_secret, "key": key, "hwid": hwid})
48
+
49
+ if res is None:
50
+ # Network error — allow offline grace period
51
+ last_seen_str = stored.get("last_seen")
52
+ if last_seen_str:
53
+ last_seen = datetime.fromisoformat(last_seen_str)
54
+ if datetime.now(timezone.utc) - last_seen < timedelta(days=OFFLINE_GRACE_DAYS):
55
+ return # within grace period, allow through
56
+ print("Unable to verify license. Check your internet connection. Exiting.")
57
+ raise SystemExit(1)
58
+
59
+ if res.get("valid"):
60
+ # Update last_seen in local store
61
+ _store.save(app_secret, {
62
+ **stored,
63
+ "last_seen": datetime.now(timezone.utc).isoformat(),
64
+ })
65
+ return
66
+
67
+ error = res.get("error", "")
68
+
69
+ if error == "hwid_mismatch":
70
+ print(
71
+ f"\n[{app_name}] This license key is registered to a different device.\n"
72
+ "To transfer it, visit: https://shieldauth.pages.dev/reset\n"
73
+ )
74
+ raise SystemExit(1)
75
+
76
+ if error in ("paused", "banned"):
77
+ print(f"\n[{app_name}] Your license has been {error}. Contact support.")
78
+ raise SystemExit(1)
79
+
80
+ if error == "expired":
81
+ print(f"\n[{app_name}] Your license has expired. Please renew.")
82
+ raise SystemExit(1)
83
+
84
+ # Key no longer valid — clear cache and re-prompt
85
+ _store.clear(app_secret)
86
+ print(f"\n[{app_name}] License key is no longer valid.")
87
+ key = _prompt.ask_for_key(app_name)
88
+ _activate(app_secret, key, hwid, app_name)
89
+
90
+
91
+ def _activate(app_secret: str, key: str, hwid: str, app_name: str) -> None:
92
+ res = _post("validate", {"app_secret": app_secret, "key": key, "hwid": hwid})
93
+
94
+ if res is None:
95
+ print("Unable to reach ShieldAuth servers. Exiting.")
96
+ raise SystemExit(1)
97
+
98
+ if res.get("valid"):
99
+ _store.save(app_secret, {
100
+ "key": key,
101
+ "app_name": res.get("app_name", app_name),
102
+ "last_seen": datetime.now(timezone.utc).isoformat(),
103
+ })
104
+ return
105
+
106
+ error = res.get("error", "")
107
+ app_name = res.get("app_name", app_name)
108
+
109
+ if error == "hwid_mismatch":
110
+ print(
111
+ f"\n[{app_name}] This key is already activated on another device.\n"
112
+ "Visit https://shieldauth.pages.dev/reset to transfer it.\n"
113
+ )
114
+ elif error == "key_not_found":
115
+ print(f"\n[{app_name}] Invalid license key. Please check and try again.")
116
+ elif error in ("paused", "banned"):
117
+ print(f"\n[{app_name}] This license key has been {error}.")
118
+ elif error == "expired":
119
+ print(f"\n[{app_name}] This license key has expired.")
120
+ else:
121
+ print(f"\n[{app_name}] License verification failed: {error}")
122
+
123
+ raise SystemExit(1)
@@ -0,0 +1,37 @@
1
+ Metadata-Version: 2.4
2
+ Name: shieldauth-key
3
+ Version: 0.1.0
4
+ Summary: License key protection for Python apps — one line of code
5
+ License: MIT
6
+ Project-URL: Homepage, https://shieldauth.pages.dev
7
+ Project-URL: Source, https://github.com/shieldauth/shieldauth-python
8
+ Requires-Python: >=3.8
9
+ Description-Content-Type: text/markdown
10
+ Requires-Dist: requests>=2.28
11
+
12
+ # shieldauth
13
+
14
+ License key protection for Python apps — one line of code.
15
+
16
+ ## Install
17
+
18
+ ```bash
19
+ pip install shieldauth
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ```python
25
+ import shieldauth
26
+ shieldauth.verify("sk_live_xxxx")
27
+
28
+ # Your app code here — only runs if license is valid
29
+ print("Welcome!")
30
+ ```
31
+
32
+ On first run, the user is prompted for their license key. It's saved locally so they're never asked again.
33
+
34
+ ## Links
35
+
36
+ - Dashboard: https://shieldauth.pages.dev
37
+ - Docs: https://shieldauth.pages.dev/docs
@@ -0,0 +1,12 @@
1
+ README.md
2
+ pyproject.toml
3
+ shieldauth/__init__.py
4
+ shieldauth/_hwid.py
5
+ shieldauth/_prompt.py
6
+ shieldauth/_store.py
7
+ shieldauth/_verify.py
8
+ shieldauth_key.egg-info/PKG-INFO
9
+ shieldauth_key.egg-info/SOURCES.txt
10
+ shieldauth_key.egg-info/dependency_links.txt
11
+ shieldauth_key.egg-info/requires.txt
12
+ shieldauth_key.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ requests>=2.28
@@ -0,0 +1 @@
1
+ shieldauth