Skip to content

Commit debf169

Browse files
committed
Update Python example with lockout helpers and CI
1 parent fd66390 commit debf169

File tree

4 files changed

+187
-52
lines changed

4 files changed

+187
-52
lines changed

.github/workflows/ci.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: ci
2+
3+
on:
4+
push:
5+
branches: [ "main" ]
6+
pull_request:
7+
branches: [ "main" ]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v4
14+
- name: Set up Python
15+
uses: actions/setup-python@v5
16+
with:
17+
python-version: "3.11"
18+
- name: Install deps
19+
run: |
20+
python -m pip install --upgrade pip
21+
pip install -r requirements.txt
22+
- name: Lint (syntax)
23+
run: |
24+
python -m py_compile keyauth.py main.py

README.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
KeyAuth Python example SDK for https://keyauth.cc license key API auth.
44

5+
Credits: Nigel (Discord: chefendpoint, Telegram: ELF_Nigel)
6+
57
## **Bugs**
68

79
If you are using our example with no significant changes, and you are having problems, please Report Bug here https://keyauth.cc/app/?page=forms
@@ -31,7 +33,29 @@ Thank you for your compliance, we work hard on the development of KeyAuth and do
3133
KeyAuth is a powerful cloud-based authentication system designed to protect your software from piracy and unauthorized access. With KeyAuth, you can implement secure licensing, user management, and subscription systems in minutes. Client SDKs available for [C#](https://github.com/KeyAuth/KeyAuth-CSHARP-Example), [C++](https://github.com/KeyAuth/KeyAuth-CPP-Example), [Python](https://github.com/KeyAuth/KeyAuth-Python-Example), [Java](https://github.com/KeyAuth-Archive/KeyAuth-JAVA-api), [JavaScript](https://github.com/mazkdevf/KeyAuth-JS-Example), [VB.NET](https://github.com/KeyAuth/KeyAuth-VB-Example), [PHP](https://github.com/KeyAuth/KeyAuth-PHP-Example), [Rust](https://github.com/KeyAuth/KeyAuth-Rust-Example), [Go](https://github.com/mazkdevf/KeyAuth-Go-Example), [Lua](https://github.com/mazkdevf/KeyAuth-Lua-Examples), [Ruby](https://github.com/mazkdevf/KeyAuth-Ruby-Example), and [Perl](https://github.com/mazkdevf/KeyAuth-Perl-Example). KeyAuth has several unique features such as memory streaming, webhook function where you can send requests to API without leaking the API, discord webhook notifications, ban the user securely through the application at your discretion. Feel free to join https://t.me/keyauth if you have questions or suggestions.
3234

3335
> [!TIP]
34-
> https://vaultcord.com FREE Discord bot to Backup server, members, channels, messages & more. Custom verify page, block alt accounts, VPNs & more.
36+
> Use the official KeyAuth examples and docs when troubleshooting to ensure your local code matches the latest API expectations.
37+
38+
## Optional client-side delays & lockout helpers
39+
40+
These helpers are client-side only and meant to slow down abuse. They do not replace server-side enforcement.
41+
42+
```py
43+
# Delay helpers
44+
api.init_fail_delay()
45+
api.bad_input_delay()
46+
api.close_delay()
47+
48+
# Lockout helpers
49+
if api.lockout_active():
50+
print(f"Locked out, try again in {api.lockout_remaining_ms()} ms")
51+
exit(1)
52+
53+
# On failed login attempt:
54+
api.record_login_fail()
55+
56+
# On successful login:
57+
api.reset_lockout()
58+
```
3559

3660
## **Customer connection issues?**
3761

keyauth.py

Lines changed: 99 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import os
2-
import json as jsond # json
3-
import time # sleep before exit
4-
import binascii # hex encoding
5-
import platform # check platform
6-
import subprocess # needed for mac device
1+
import os
2+
import json as jsond # json
3+
import time # sleep before exit
4+
import binascii # hex encoding
5+
import platform # check platform
6+
import subprocess # needed for mac device
7+
import threading
78
import qrcode
89
from datetime import datetime, timezone, timedelta
910
from discord_interactions import verify_key # used for signature verification
@@ -28,11 +29,26 @@
2829
os._exit(1)
2930

3031

31-
class api:
32-
33-
name = ownerid = version = hash_to_check = ""
34-
35-
def __init__(self, name, ownerid, version, hash_to_check):
32+
class api:
33+
34+
name = ownerid = version = hash_to_check = ""
35+
init_fail_delay_ms = 1500
36+
bad_input_delay_ms = 3000
37+
close_delay_ms = 5000
38+
_lockout_lock = threading.Lock()
39+
_lockout_state = {
40+
"fail_count": 0,
41+
"first_fail_at": 0.0,
42+
"locked_until": 0.0,
43+
"last_fail_at": 0.0,
44+
}
45+
lockout_settings = {
46+
"max_attempts": 5,
47+
"window_seconds": 120.0,
48+
"lockout_seconds": 300.0,
49+
}
50+
51+
def __init__(self, name, ownerid, version, hash_to_check):
3652
if len(ownerid) != 10:
3753
print("Visit https://keyauth.cc/app/, copy Pthon code, and replace code in main.py with that")
3854
time.sleep(3)
@@ -43,17 +59,17 @@ def __init__(self, name, ownerid, version, hash_to_check):
4359
self.ownerid = ownerid
4460

4561
self.version = version
46-
self.hash_to_check = hash_to_check
47-
self.init()
62+
self.hash_to_check = hash_to_check
63+
self.init()
4864

4965
sessionid = enckey = ""
5066
initialized = False
5167

52-
def init(self):
53-
if self.sessionid != "":
54-
print("You've already initialized!")
55-
time.sleep(3)
56-
os._exit(1)
68+
def init(self):
69+
if self.sessionid != "":
70+
print("You've already initialized!")
71+
time.sleep(3)
72+
os._exit(1)
5773

5874
post_data = {
5975
"type": "init",
@@ -89,8 +105,70 @@ def init(self):
89105
time.sleep(3)
90106
os._exit(1)
91107

92-
self.sessionid = json["sessionid"]
93-
self.initialized = True
108+
self.sessionid = json["sessionid"]
109+
self.initialized = True
110+
111+
@staticmethod
112+
def init_fail_delay():
113+
time.sleep(api.init_fail_delay_ms / 1000.0)
114+
115+
@staticmethod
116+
def bad_input_delay():
117+
time.sleep(api.bad_input_delay_ms / 1000.0)
118+
119+
@staticmethod
120+
def close_delay():
121+
time.sleep(api.close_delay_ms / 1000.0)
122+
123+
@staticmethod
124+
def lockout_state_snapshot():
125+
with api._lockout_lock:
126+
return dict(api._lockout_state)
127+
128+
@staticmethod
129+
def lockout_active():
130+
with api._lockout_lock:
131+
locked_until = api._lockout_state["locked_until"]
132+
if locked_until <= 0:
133+
return False
134+
return time.time() < locked_until
135+
136+
@staticmethod
137+
def lockout_remaining_ms():
138+
with api._lockout_lock:
139+
locked_until = api._lockout_state["locked_until"]
140+
if locked_until <= 0:
141+
return 0
142+
remaining = locked_until - time.time()
143+
if remaining < 0:
144+
return 0
145+
return int(remaining * 1000)
146+
147+
@staticmethod
148+
def record_login_fail():
149+
with api._lockout_lock:
150+
now = time.time()
151+
api._lockout_state["last_fail_at"] = now
152+
153+
first_fail_at = api._lockout_state["first_fail_at"]
154+
if first_fail_at <= 0 or (now - first_fail_at) > api.lockout_settings["window_seconds"]:
155+
api._lockout_state["first_fail_at"] = now
156+
api._lockout_state["fail_count"] = 1
157+
return
158+
159+
api._lockout_state["fail_count"] += 1
160+
if api._lockout_state["fail_count"] >= api.lockout_settings["max_attempts"]:
161+
api._lockout_state["locked_until"] = now + api.lockout_settings["lockout_seconds"]
162+
163+
@staticmethod
164+
def reset_lockout():
165+
with api._lockout_lock:
166+
api._lockout_state = {
167+
"fail_count": 0,
168+
"first_fail_at": 0.0,
169+
"locked_until": 0.0,
170+
"last_fail_at": 0.0,
171+
}
94172

95173
def register(self, user, password, license, hwid=None):
96174
self.checkinit()
@@ -692,4 +770,4 @@ def get_hwid():
692770
output = subprocess.Popen("ioreg -l | grep IOPlatformSerialNumber", stdout=subprocess.PIPE, shell=True).communicate()[0]
693771
serial = output.decode().split('=', 1)[1].replace(' ', '')
694772
hwid = serial[1:-2]
695-
return hwid
773+
return hwid

main.py

Lines changed: 39 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -53,37 +53,46 @@ def getchecksum():
5353
hash_to_check = getchecksum()
5454
)
5555

56-
def answer():
57-
try:
58-
print("""1.Login
59-
2.Register
60-
3.Upgrade
61-
4.License Key Only
62-
""")
56+
def answer():
57+
try:
58+
if api.lockout_active():
59+
print(f"Locked out, try again in {api.lockout_remaining_ms()} ms")
60+
api.close_delay()
61+
os._exit(1)
62+
63+
print("""1.Login
64+
2.Register
65+
3.Upgrade
66+
4.License Key Only
67+
""")
6368
ans = input("Select Option: ")
64-
if ans == "1":
65-
user = input('Provide username: ')
66-
password = input('Provide password: ')
67-
code = input('Enter 2fa code: (not using 2fa? Just press enter)')
68-
keyauthapp.login(user, password, code)
69-
elif ans == "2":
70-
user = input('Provide username: ')
71-
password = input('Provide password: ')
72-
license = input('Provide License: ')
73-
keyauthapp.register(user, password, license)
74-
elif ans == "3":
75-
user = input('Provide username: ')
76-
license = input('Provide License: ')
77-
keyauthapp.upgrade(user, license)
78-
elif ans == "4":
79-
key = input('Enter your license: ')
80-
code = input('Enter 2fa code: (not using 2fa? Just press enter)')
81-
keyauthapp.license(key, code)
82-
else:
83-
print("\nInvalid option")
84-
sleep(1)
85-
clear()
86-
answer()
69+
if ans == "1":
70+
user = input('Provide username: ')
71+
password = input('Provide password: ')
72+
code = input('Enter 2fa code: (not using 2fa? Just press enter)')
73+
keyauthapp.login(user, password, code)
74+
api.reset_lockout()
75+
elif ans == "2":
76+
user = input('Provide username: ')
77+
password = input('Provide password: ')
78+
license = input('Provide License: ')
79+
keyauthapp.register(user, password, license)
80+
api.reset_lockout()
81+
elif ans == "3":
82+
user = input('Provide username: ')
83+
license = input('Provide License: ')
84+
keyauthapp.upgrade(user, license)
85+
api.reset_lockout()
86+
elif ans == "4":
87+
key = input('Enter your license: ')
88+
code = input('Enter 2fa code: (not using 2fa? Just press enter)')
89+
keyauthapp.license(key, code)
90+
api.reset_lockout()
91+
else:
92+
print("\nInvalid option")
93+
api.bad_input_delay()
94+
clear()
95+
answer()
8796
except KeyboardInterrupt:
8897
os._exit(1)
8998

0 commit comments

Comments
 (0)