pypomes-jwt 1.1.1__tar.gz → 1.1.3__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.
Potentially problematic release.
This version of pypomes-jwt might be problematic. Click here for more details.
- {pypomes_jwt-1.1.1 → pypomes_jwt-1.1.3}/PKG-INFO +2 -2
- {pypomes_jwt-1.1.1 → pypomes_jwt-1.1.3}/pyproject.toml +2 -2
- {pypomes_jwt-1.1.1 → pypomes_jwt-1.1.3}/src/pypomes_jwt/jwt_configuration.py +1 -22
- {pypomes_jwt-1.1.1 → pypomes_jwt-1.1.3}/src/pypomes_jwt/jwt_pomes.py +19 -19
- {pypomes_jwt-1.1.1 → pypomes_jwt-1.1.3}/src/pypomes_jwt/jwt_registry.py +38 -32
- {pypomes_jwt-1.1.1 → pypomes_jwt-1.1.3}/.gitignore +0 -0
- {pypomes_jwt-1.1.1 → pypomes_jwt-1.1.3}/LICENSE +0 -0
- {pypomes_jwt-1.1.1 → pypomes_jwt-1.1.3}/README.md +0 -0
- {pypomes_jwt-1.1.1 → pypomes_jwt-1.1.3}/src/pypomes_jwt/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pypomes_jwt
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.3
|
|
4
4
|
Summary: A collection of Python pomes, penyeach (JWT module)
|
|
5
5
|
Project-URL: Homepage, https://github.com/TheWiseCoder/PyPomes-JWT
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/TheWiseCoder/PyPomes-JWT/issues
|
|
@@ -12,5 +12,5 @@ Classifier: Programming Language :: Python :: 3
|
|
|
12
12
|
Requires-Python: >=3.12
|
|
13
13
|
Requires-Dist: cryptography>=44.0.2
|
|
14
14
|
Requires-Dist: pyjwt>=2.10.1
|
|
15
|
-
Requires-Dist: pypomes-core>=1.9.
|
|
15
|
+
Requires-Dist: pypomes-core>=1.9.9
|
|
16
16
|
Requires-Dist: pypomes-db>=2.0.9
|
|
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "pypomes_jwt"
|
|
9
|
-
version = "1.1.
|
|
9
|
+
version = "1.1.3"
|
|
10
10
|
authors = [
|
|
11
11
|
{ name="GT Nunes", email="wisecoder01@gmail.com" }
|
|
12
12
|
]
|
|
@@ -21,7 +21,7 @@ classifiers = [
|
|
|
21
21
|
dependencies = [
|
|
22
22
|
"PyJWT>=2.10.1",
|
|
23
23
|
"cryptography>=44.0.2",
|
|
24
|
-
"pypomes_core>=1.9.
|
|
24
|
+
"pypomes_core>=1.9.9",
|
|
25
25
|
"pypomes_db>=2.0.9"
|
|
26
26
|
]
|
|
27
27
|
|
|
@@ -6,9 +6,8 @@ from pypomes_core import (
|
|
|
6
6
|
APP_PREFIX,
|
|
7
7
|
env_get_str, env_get_bytes, env_get_int
|
|
8
8
|
)
|
|
9
|
-
from pypomes_db import DbEngine
|
|
9
|
+
from pypomes_db import DbEngine
|
|
10
10
|
from secrets import token_bytes
|
|
11
|
-
from sys import stderr
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
# recommended: allow the encode and decode keys to be generated anew when app starts
|
|
@@ -57,29 +56,9 @@ class JwtDbConfig(Enum):
|
|
|
57
56
|
Parameters for JWT databse connection.
|
|
58
57
|
"""
|
|
59
58
|
ENGINE: str = DbEngine(env_get_str(key=f"{APP_PREFIX}_JWT_DB_ENGINE"))
|
|
60
|
-
CLIENT: str = env_get_str(key=f"{APP_PREFIX}_JWT_DB_CLIENT") # for Oracle, only
|
|
61
|
-
DRIVER: str = env_get_str(key=f"{APP_PREFIX}_JWT_DB_DRIVER") # for SQLServer, only
|
|
62
|
-
NAME: str = env_get_str(key=f"{APP_PREFIX}_JWT_DB_NAME")
|
|
63
|
-
HOST: str = env_get_str(key=f"{APP_PREFIX}_JWT_DB_HOST")
|
|
64
|
-
PORT: int = env_get_int(key=f"{APP_PREFIX}_JWT_DB_PORT")
|
|
65
|
-
USER: str = env_get_str(key=f"{APP_PREFIX}_JWT_DB_USER")
|
|
66
|
-
PWD: str = env_get_str(key=f"{APP_PREFIX}_JWT_DB_PWD")
|
|
67
59
|
TABLE: str = env_get_str(key=f"{APP_PREFIX}_JWT_DB_TABLE")
|
|
68
60
|
COL_ACCOUNT: str = env_get_str(key=f"{APP_PREFIX}_JWT_DB_COL_ACCOUNT")
|
|
69
61
|
COL_ALGORITHM: str = env_get_str(key=f"{APP_PREFIX}_JWT_DB_COL_ALGORITHM")
|
|
70
62
|
COL_DECODER: str = env_get_str(key=f"{APP_PREFIX}_JWT_DB_COL_DECODER")
|
|
71
63
|
COL_KID: str = env_get_str(key=f"{APP_PREFIX}_JWT_DB_COL_KID")
|
|
72
64
|
COL_TOKEN: str = env_get_str(key=f"{APP_PREFIX}_JWT_DB_COL_TOKEN")
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
# define and validate the database engine
|
|
76
|
-
# noinspection PyTypeChecker
|
|
77
|
-
if not db_setup(engine=DbEngine(JwtDbConfig.ENGINE),
|
|
78
|
-
db_name=JwtDbConfig.NAME,
|
|
79
|
-
db_user=JwtDbConfig.USER,
|
|
80
|
-
db_pwd=JwtDbConfig.PWD,
|
|
81
|
-
db_host=JwtDbConfig.HOST,
|
|
82
|
-
db_port=JwtDbConfig.PORT,
|
|
83
|
-
db_client=JwtDbConfig.CLIENT,
|
|
84
|
-
db_driver=JwtDbConfig.DRIVER):
|
|
85
|
-
stderr.write("Invalid database parameters\n")
|
|
@@ -75,8 +75,8 @@ def jwt_assert_account(account_id: str) -> bool:
|
|
|
75
75
|
|
|
76
76
|
def jwt_set_account(account_id: str,
|
|
77
77
|
claims: dict[str, Any],
|
|
78
|
-
access_max_age: int = JwtConfig.ACCESS_MAX_AGE,
|
|
79
|
-
refresh_max_age: int = JwtConfig.REFRESH_MAX_AGE,
|
|
78
|
+
access_max_age: int = JwtConfig.ACCESS_MAX_AGE.value,
|
|
79
|
+
refresh_max_age: int = JwtConfig.REFRESH_MAX_AGE.value,
|
|
80
80
|
grace_interval: int = None,
|
|
81
81
|
logger: Logger = None) -> None:
|
|
82
82
|
"""
|
|
@@ -177,16 +177,15 @@ def jwt_validate_token(errors: list[str] | None,
|
|
|
177
177
|
elif token_kid and len(token_kid) > 1 and \
|
|
178
178
|
token_kid[0:1] in ["A", "R"] and token_kid[1:].isdigit():
|
|
179
179
|
# token was likely issued locally
|
|
180
|
-
where_data: dict[str, Any] = {
|
|
181
|
-
str(JwtDbConfig.COL_KID): int(token_kid[1:])
|
|
182
|
-
}
|
|
180
|
+
where_data: dict[str, Any] = {JwtDbConfig.COL_KID.value: int(token_kid[1:])}
|
|
183
181
|
if account_id:
|
|
184
|
-
where_data[
|
|
182
|
+
where_data[JwtDbConfig.COL_ACCOUNT.value] = account_id
|
|
185
183
|
recs: list[tuple[str]] = db_select(errors=op_errors,
|
|
186
|
-
sel_stmt=f"SELECT {JwtDbConfig.COL_ALGORITHM}, "
|
|
187
|
-
f"{JwtDbConfig.COL_DECODER} "
|
|
188
|
-
f"FROM {JwtDbConfig.TABLE}",
|
|
184
|
+
sel_stmt=f"SELECT {JwtDbConfig.COL_ALGORITHM.value}, "
|
|
185
|
+
f"{JwtDbConfig.COL_DECODER.value} "
|
|
186
|
+
f"FROM {JwtDbConfig.TABLE.value}",
|
|
189
187
|
where_data=where_data,
|
|
188
|
+
engine=JwtDbConfig.ENGINE.value,
|
|
190
189
|
logger=logger)
|
|
191
190
|
if recs:
|
|
192
191
|
token_alg = recs[0][0]
|
|
@@ -199,10 +198,8 @@ def jwt_validate_token(errors: list[str] | None,
|
|
|
199
198
|
logger.error(msg="Token not in the database")
|
|
200
199
|
op_errors.append("Invalid token")
|
|
201
200
|
else:
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
# noinspection PyTypeChecker
|
|
205
|
-
token_decoder = JwtConfig.DECODING_KEY
|
|
201
|
+
token_alg = JwtConfig.DEFAULT_ALGORITHM.value
|
|
202
|
+
token_decoder = JwtConfig.DECODING_KEY.value
|
|
206
203
|
|
|
207
204
|
# validate the token
|
|
208
205
|
if not op_errors:
|
|
@@ -282,11 +279,12 @@ def jwt_revoke_token(errors: list[str] | None,
|
|
|
282
279
|
op_errors.append("Invalid token")
|
|
283
280
|
else:
|
|
284
281
|
db_delete(errors=op_errors,
|
|
285
|
-
delete_stmt=f"DELETE FROM {JwtDbConfig.TABLE}",
|
|
282
|
+
delete_stmt=f"DELETE FROM {JwtDbConfig.TABLE.value}",
|
|
286
283
|
where_data={
|
|
287
|
-
|
|
288
|
-
|
|
284
|
+
JwtDbConfig.COL_KID.value: int(token_kid[1:]),
|
|
285
|
+
JwtDbConfig.COL_ACCOUNT.value: account_id
|
|
289
286
|
},
|
|
287
|
+
engine=JwtDbConfig.ENGINE.value,
|
|
290
288
|
logger=logger)
|
|
291
289
|
if op_errors:
|
|
292
290
|
if logger:
|
|
@@ -452,15 +450,17 @@ def jwt_refresh_tokens(errors: list[str] | None,
|
|
|
452
450
|
# start the database transaction
|
|
453
451
|
db_conn: Any = db_connect(errors=op_errors,
|
|
454
452
|
autocommit=False,
|
|
453
|
+
engine=JwtDbConfig.ENGINE.value,
|
|
455
454
|
logger=logger)
|
|
456
455
|
if db_conn:
|
|
457
456
|
# delete current refresh token
|
|
458
457
|
db_delete(errors=op_errors,
|
|
459
|
-
delete_stmt=f"DELETE FROM {JwtDbConfig.TABLE}",
|
|
458
|
+
delete_stmt=f"DELETE FROM {JwtDbConfig.TABLE.value}",
|
|
460
459
|
where_data={
|
|
461
|
-
|
|
462
|
-
|
|
460
|
+
JwtDbConfig.COL_KID.value: int(token_kid[1:]),
|
|
461
|
+
JwtDbConfig.COL_ACCOUNT.value: account_id
|
|
463
462
|
},
|
|
463
|
+
engine=JwtDbConfig.ENGINE.value,
|
|
464
464
|
connection=db_conn,
|
|
465
465
|
committable=False,
|
|
466
466
|
logger=logger)
|
|
@@ -139,8 +139,9 @@ class JwtRegistry:
|
|
|
139
139
|
|
|
140
140
|
# remove from database
|
|
141
141
|
db_delete(errors=None,
|
|
142
|
-
delete_stmt=f"DELETE FROM {JwtDbConfig.
|
|
143
|
-
where_data={
|
|
142
|
+
delete_stmt=f"DELETE FROM {JwtDbConfig.value}",
|
|
143
|
+
where_data={JwtDbConfig.COL_ACCOUNT.value: account_id},
|
|
144
|
+
engine=JwtDbConfig.ENGINE.value,
|
|
144
145
|
logger=logger)
|
|
145
146
|
if logger:
|
|
146
147
|
if account_data:
|
|
@@ -214,8 +215,8 @@ class JwtRegistry:
|
|
|
214
215
|
tz=timezone.utc).isoformat()
|
|
215
216
|
# may raise an exception
|
|
216
217
|
return jwt.encode(payload=current_claims,
|
|
217
|
-
key=JwtConfig.ENCODING_KEY,
|
|
218
|
-
algorithm=JwtConfig.DEFAULT_ALGORITHM,
|
|
218
|
+
key=JwtConfig.ENCODING_KEY.value,
|
|
219
|
+
algorithm=JwtConfig.DEFAULT_ALGORITHM.value,
|
|
219
220
|
headers={"kid": nature})
|
|
220
221
|
|
|
221
222
|
def issue_tokens(self,
|
|
@@ -275,13 +276,14 @@ class JwtRegistry:
|
|
|
275
276
|
tz=timezone.utc).isoformat()
|
|
276
277
|
# may raise an exception
|
|
277
278
|
refresh_token: str = jwt.encode(payload=current_claims,
|
|
278
|
-
key=JwtConfig.ENCODING_KEY,
|
|
279
|
-
algorithm=JwtConfig.DEFAULT_ALGORITHM,
|
|
279
|
+
key=JwtConfig.ENCODING_KEY.value,
|
|
280
|
+
algorithm=JwtConfig.DEFAULT_ALGORITHM.value,
|
|
280
281
|
headers={"kid": "R0"})
|
|
281
282
|
|
|
282
283
|
# make sure to have a database connection
|
|
283
284
|
curr_conn: Any = db_conn or db_connect(errors=errors,
|
|
284
285
|
autocommit=False,
|
|
286
|
+
engine=JwtDbConfig.ENGINE.value,
|
|
285
287
|
logger=logger)
|
|
286
288
|
if curr_conn:
|
|
287
289
|
# persist the candidate token (may raise an exception)
|
|
@@ -291,14 +293,15 @@ class JwtRegistry:
|
|
|
291
293
|
logger=logger)
|
|
292
294
|
# issue the definitive refresh token
|
|
293
295
|
refresh_token = jwt.encode(payload=current_claims,
|
|
294
|
-
key=JwtConfig.ENCODING_KEY,
|
|
295
|
-
algorithm=JwtConfig.DEFAULT_ALGORITHM,
|
|
296
|
+
key=JwtConfig.ENCODING_KEY.value,
|
|
297
|
+
algorithm=JwtConfig.DEFAULT_ALGORITHM.value,
|
|
296
298
|
headers={"kid": f"R{token_id}"})
|
|
297
299
|
# persist it
|
|
298
300
|
db_update(errors=errors,
|
|
299
|
-
update_stmt=f"UPDATE {JwtDbConfig.TABLE}",
|
|
300
|
-
update_data={
|
|
301
|
-
where_data={
|
|
301
|
+
update_stmt=f"UPDATE {JwtDbConfig.TABLE.value}",
|
|
302
|
+
update_data={JwtDbConfig.COL_TOKEN.value: refresh_token},
|
|
303
|
+
where_data={JwtDbConfig.COL_KID.value: token_id},
|
|
304
|
+
engine=JwtDbConfig.ENGINE.value,
|
|
302
305
|
connection=curr_conn,
|
|
303
306
|
committable=False,
|
|
304
307
|
logger=logger)
|
|
@@ -320,8 +323,8 @@ class JwtRegistry:
|
|
|
320
323
|
current_claims["exp"] = just_now + account_data.get("access-max-age")
|
|
321
324
|
# may raise an exception
|
|
322
325
|
access_token: str = jwt.encode(payload=current_claims,
|
|
323
|
-
key=JwtConfig.ENCODING_KEY,
|
|
324
|
-
algorithm=JwtConfig.DEFAULT_ALGORITHM,
|
|
326
|
+
key=JwtConfig.ENCODING_KEY.value,
|
|
327
|
+
algorithm=JwtConfig.DEFAULT_ALGORITHM.value,
|
|
325
328
|
headers={"kid": f"A{token_id}"})
|
|
326
329
|
# return the token data
|
|
327
330
|
return {
|
|
@@ -379,9 +382,10 @@ def _jwt_persist_token(account_id: str,
|
|
|
379
382
|
# noinspection PyTypeChecker
|
|
380
383
|
recs: list[tuple[int, str, str, str]] = \
|
|
381
384
|
db_select(errors=errors,
|
|
382
|
-
sel_stmt=f"SELECT {JwtDbConfig.COL_KID}, {JwtDbConfig.COL_TOKEN} "
|
|
383
|
-
f"FROM {JwtDbConfig.TABLE}",
|
|
384
|
-
where_data={
|
|
385
|
+
sel_stmt=f"SELECT {JwtDbConfig.COL_KID.value}, {JwtDbConfig.COL_TOKEN.value} "
|
|
386
|
+
f"FROM {JwtDbConfig.TABLE.value}",
|
|
387
|
+
where_data={JwtDbConfig.COL_ACCOUNT.value: account_id},
|
|
388
|
+
engine=JwtDbConfig.ENGINE.value,
|
|
385
389
|
connection=db_conn,
|
|
386
390
|
committable=False,
|
|
387
391
|
logger=logger)
|
|
@@ -422,8 +426,9 @@ def _jwt_persist_token(account_id: str,
|
|
|
422
426
|
# remove expired tokens from persistence
|
|
423
427
|
if expired:
|
|
424
428
|
db_delete(errors=errors,
|
|
425
|
-
delete_stmt=f"DELETE FROM {JwtDbConfig.TABLE}",
|
|
426
|
-
where_data={
|
|
429
|
+
delete_stmt=f"DELETE FROM {JwtDbConfig.TABLE.value}",
|
|
430
|
+
where_data={JwtDbConfig.COL_KID.value: expired},
|
|
431
|
+
engine=JwtDbConfig.ENGINE.value,
|
|
427
432
|
connection=db_conn,
|
|
428
433
|
committable=False,
|
|
429
434
|
logger=logger)
|
|
@@ -433,12 +438,12 @@ def _jwt_persist_token(account_id: str,
|
|
|
433
438
|
logger.debug(msg=f"{len(expired)} tokens of account "
|
|
434
439
|
f"'{account_id}' removed from storage")
|
|
435
440
|
|
|
436
|
-
|
|
437
|
-
if 0 < JwtConfig.ACCOUNT_LIMIT <= len(recs) - len(expired):
|
|
441
|
+
if 0 < JwtConfig.ACCOUNT_LIMIT.value <= len(recs) - len(expired):
|
|
438
442
|
# delete the oldest token to make way for the new one
|
|
439
443
|
db_delete(errors=errors,
|
|
440
|
-
delete_stmt=f"DELETE FROM {JwtDbConfig.TABLE}",
|
|
441
|
-
where_data={
|
|
444
|
+
delete_stmt=f"DELETE FROM {JwtDbConfig.TABLE.value}",
|
|
445
|
+
where_data={JwtDbConfig.COL_KID.value: oldest_id},
|
|
446
|
+
engine=JwtDbConfig.ENGINE.value,
|
|
442
447
|
connection=db_conn,
|
|
443
448
|
committable=False,
|
|
444
449
|
logger=logger)
|
|
@@ -448,15 +453,15 @@ def _jwt_persist_token(account_id: str,
|
|
|
448
453
|
logger.debug(msg="Oldest active token of account "
|
|
449
454
|
f"'{account_id}' removed from storage")
|
|
450
455
|
# persist token
|
|
451
|
-
# noinspection PyTypeChecker
|
|
452
456
|
db_insert(errors=errors,
|
|
453
|
-
insert_stmt=f"INSERT INTO {JwtDbConfig.TABLE}",
|
|
457
|
+
insert_stmt=f"INSERT INTO {JwtDbConfig.TABLE.value}",
|
|
454
458
|
insert_data={
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
+
JwtDbConfig.COL_ACCOUNT.value: account_id,
|
|
460
|
+
JwtDbConfig.COL_TOKEN.value: jwt_token,
|
|
461
|
+
JwtDbConfig.COL_ALGORITHM.value: JwtConfig.DEFAULT_ALGORITHM.value,
|
|
462
|
+
JwtDbConfig.COL_DECODER.value: urlsafe_b64encode(s=JwtConfig.DECODING_KEY.value).decode()
|
|
459
463
|
},
|
|
464
|
+
engine=JwtDbConfig.ENGINE.value,
|
|
460
465
|
connection=db_conn,
|
|
461
466
|
committable=False,
|
|
462
467
|
logger=logger)
|
|
@@ -467,14 +472,15 @@ def _jwt_persist_token(account_id: str,
|
|
|
467
472
|
# HAZARD: JWT_DB_COL_TOKEN's column type might prevent it for being used in a WHERE clause
|
|
468
473
|
where_clause: str | None = None
|
|
469
474
|
if existing_ids:
|
|
470
|
-
where_clause = f"{JwtDbConfig.COL_KID} NOT IN {existing_ids}"
|
|
475
|
+
where_clause = f"{JwtDbConfig.COL_KID.value} NOT IN {existing_ids}"
|
|
471
476
|
where_clause = where_clause.replace("[", "(", 1).replace("]", ")", 1)
|
|
472
477
|
reply: list[tuple[int]] = db_select(errors=errors,
|
|
473
|
-
sel_stmt=f"SELECT {JwtDbConfig.COL_KID} "
|
|
474
|
-
f"FROM {JwtDbConfig.TABLE}",
|
|
478
|
+
sel_stmt=f"SELECT {JwtDbConfig.COL_KID.value} "
|
|
479
|
+
f"FROM {JwtDbConfig.TABLE.value}",
|
|
475
480
|
where_clause=where_clause,
|
|
476
|
-
where_data={
|
|
481
|
+
where_data={JwtDbConfig.COL_ACCOUNT.value: account_id},
|
|
477
482
|
require_count=1,
|
|
483
|
+
engine=JwtDbConfig.ENGINE.value,
|
|
478
484
|
connection=db_conn,
|
|
479
485
|
committable=False,
|
|
480
486
|
logger=logger)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|