pypomes-jwt 1.0.8__tar.gz → 1.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.

Potentially problematic release.


This version of pypomes-jwt might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pypomes_jwt
3
- Version: 1.0.8
3
+ Version: 1.1.0
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.2
16
- Requires-Dist: pypomes-db>=2.0.3
15
+ Requires-Dist: pypomes-core>=1.9.6
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.0.8"
9
+ version = "1.1.0"
10
10
  authors = [
11
11
  { name="GT Nunes", email="wisecoder01@gmail.com" }
12
12
  ]
@@ -21,8 +21,8 @@ classifiers = [
21
21
  dependencies = [
22
22
  "PyJWT>=2.10.1",
23
23
  "cryptography>=44.0.2",
24
- "pypomes_core>=1.9.2",
25
- "pypomes_db>=2.0.3"
24
+ "pypomes_core>=1.9.6",
25
+ "pypomes_db>=2.0.9"
26
26
  ]
27
27
 
28
28
  [project.urls]
@@ -0,0 +1,23 @@
1
+ from .jwt_constants import (
2
+ JwtParam, JwtDbParam
3
+ )
4
+ from .jwt_pomes import (
5
+ jwt_needed, jwt_verify_request,
6
+ jwt_assert_account, jwt_set_account, jwt_remove_account,
7
+ jwt_issue_token, jwt_issue_tokens, jwt_refresh_tokens,
8
+ jwt_get_claims, jwt_validate_token, jwt_revoke_token
9
+ )
10
+
11
+ __all__ = [
12
+ # jwt_constants
13
+ "JwtParam", "JwtDbParam",
14
+ # jwt_pomes
15
+ "jwt_needed", "jwt_verify_request",
16
+ "jwt_assert_account", "jwt_set_account", "jwt_remove_account",
17
+ "jwt_issue_token", "jwt_issue_tokens", "jwt_refresh_tokens",
18
+ "jwt_get_claims", "jwt_validate_token", "jwt_revoke_token"
19
+ ]
20
+
21
+ from importlib.metadata import version
22
+ __version__ = version("pypomes_jwt")
23
+ __version_info__ = tuple(int(i) for i in __version__.split(".") if i.isdigit())
@@ -0,0 +1,108 @@
1
+ from cryptography.hazmat.primitives import serialization
2
+ from cryptography.hazmat.primitives.asymmetric import rsa
3
+ from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey, RSAPublicKey
4
+ from enum import StrEnum
5
+ from pypomes_core import (
6
+ APP_PREFIX,
7
+ env_get_str, env_get_bytes, env_get_int
8
+ )
9
+ from pypomes_db import DbEngine, db_setup
10
+ from secrets import token_bytes
11
+ from sys import stderr
12
+ from typing import Any, Final
13
+
14
+
15
+ class JwtParam(StrEnum):
16
+ """
17
+ Parameters for JWT provider access.
18
+ """
19
+ ACCESS_MAX_AGE = "access-max-age"
20
+ ACCOUNT_LIMIT = "account-limit"
21
+ DECODING_KEY = "decoding-key"
22
+ DEFAULT_ALGORITHM = "default-algorithm"
23
+ ENCODING_KEY = "encoding-key"
24
+ REFRESH_MAX_AGE = "refresh-max-age"
25
+
26
+
27
+ class JwtDbParam(StrEnum):
28
+ """
29
+ Parameters for JWT databse connection.
30
+ """
31
+ CLIENT = "client"
32
+ DRIVER = "driver"
33
+ ENGINE = "engine"
34
+ NAME = "name"
35
+ HOST = "host"
36
+ PORT = "port"
37
+ USER = "user"
38
+ PWD = "pwd"
39
+ TABLE = "table"
40
+ COL_ACCOUNT = "col-account"
41
+ COL_ALGORITHM = "col-algorithm"
42
+ COL_DECODER = "col-decoder"
43
+ COL_KID = "col-kid"
44
+ COL_TOKEN = "col-token"
45
+
46
+
47
+ # recommended: allow the encode and decode keys to be generated anew when app starts
48
+ __encoding_key: bytes = env_get_bytes(key=f"{APP_PREFIX}_JWT_ENCODING_KEY",
49
+ encoding="base64url")
50
+ __decoding_key: bytes
51
+ # one of HS256, HS512, RS256, RS512
52
+ __default_algorithm: str = env_get_str(key=f"{APP_PREFIX}_JWT_DEFAULT_ALGORITHM",
53
+ def_value="RS256")
54
+ if __default_algorithm in ["HS256", "HS512"]:
55
+ if not __encoding_key:
56
+ __encoding_key = token_bytes(nbytes=32)
57
+ __decoding_key = __encoding_key
58
+ else:
59
+ __decoding_key: bytes = env_get_bytes(key=f"{APP_PREFIX}_JWT_DECODING_KEY")
60
+ if not __encoding_key or not __decoding_key:
61
+ __priv_key: RSAPrivateKey = rsa.generate_private_key(public_exponent=65537,
62
+ key_size=2048)
63
+ __encoding_key = __priv_key.private_bytes(encoding=serialization.Encoding.PEM,
64
+ format=serialization.PrivateFormat.PKCS8,
65
+ encryption_algorithm=serialization.NoEncryption())
66
+ __pub_key: RSAPublicKey = __priv_key.public_key()
67
+ __decoding_key = __pub_key.public_bytes(encoding=serialization.Encoding.PEM,
68
+ format=serialization.PublicFormat.SubjectPublicKeyInfo)
69
+
70
+ _JWT_CONFIG: Final[dict[JwtParam, Any]] = {
71
+ # recommended: between 5 min and 1 hour (set to 5 min)
72
+ JwtParam.ACCESS_MAX_AGE: env_get_int(key=f"{APP_PREFIX}_JWT_ACCESS_MAX_AGE",
73
+ def_value=300),
74
+ JwtParam.ACCOUNT_LIMIT: env_get_int(key=f"{APP_PREFIX}_JWT_ACCOUNT_LIMIT"),
75
+ JwtParam.DECODING_KEY: __decoding_key,
76
+ JwtParam.DEFAULT_ALGORITHM: __default_algorithm,
77
+ JwtParam.ENCODING_KEY: __encoding_key,
78
+ # recommended: at least 2 hours (set to 24 hours)
79
+ JwtParam.REFRESH_MAX_AGE: env_get_int(key=f"{APP_PREFIX}_JWT_REFRESH_MAX_AGE",
80
+ def_value=86400)
81
+ }
82
+ _JWT_DB: Final[JwtDbParam, Any] = {
83
+ JwtDbParam.ENGINE: DbEngine(env_get_str(key=f"{APP_PREFIX}_JWT_DB_ENGINE")),
84
+ JwtDbParam.CLIENT: env_get_str(key=f"{APP_PREFIX}_JWT_DB_CLIENT"), # for Oracle, only
85
+ JwtDbParam.DRIVER: env_get_str(key=f"{APP_PREFIX}_JWT_DB_DRIVER"), # for SQLServer, only
86
+ JwtDbParam.NAME: env_get_str(key=f"{APP_PREFIX}_JWT_DB_NAME"),
87
+ JwtDbParam.HOST: env_get_str(key=f"{APP_PREFIX}_JWT_DB_HOST"),
88
+ JwtDbParam.PORT: env_get_int(key=f"{APP_PREFIX}_JWT_DB_PORT"),
89
+ JwtDbParam.USER: env_get_str(key=f"{APP_PREFIX}_JWT_DB_USER"),
90
+ JwtDbParam.PWD: env_get_str(key=f"{APP_PREFIX}_JWT_DB_PWD"),
91
+ JwtDbParam.TABLE: env_get_str(key=f"{APP_PREFIX}_JWT_DB_TABLE"),
92
+ JwtDbParam.COL_ACCOUNT: env_get_str(key=f"{APP_PREFIX}_JWT_DB_COL_ACCOUNT"),
93
+ JwtDbParam.COL_ALGORITHM: env_get_str(key=f"{APP_PREFIX}_JWT_DB_COL_ALGORITHM"),
94
+ JwtDbParam.COL_DECODER: env_get_str(key=f"{APP_PREFIX}_JWT_DB_COL_DECODER"),
95
+ JwtDbParam.COL_KID: env_get_str(key=f"{APP_PREFIX}_JWT_DB_COL_KID"),
96
+ JwtDbParam.COL_TOKEN: env_get_str(key=f"{APP_PREFIX}_JWT_DB_COL_TOKEN")
97
+ }
98
+
99
+ # define and validate the database engine
100
+ if not db_setup(engine=_JWT_DB[JwtDbParam.ENGINE],
101
+ db_name=_JWT_DB[JwtDbParam.NAME],
102
+ db_user=_JWT_DB[JwtDbParam.USER],
103
+ db_pwd=_JWT_DB[JwtDbParam.PWD],
104
+ db_host=_JWT_DB[JwtDbParam.HOST],
105
+ db_port=_JWT_DB[JwtDbParam.PORT],
106
+ db_client=_JWT_DB[JwtDbParam.CLIENT],
107
+ db_driver=_JWT_DB[JwtDbParam.DRIVER]):
108
+ stderr.write("Invalid database parameters\n")
@@ -9,15 +9,12 @@ from pypomes_db import (
9
9
  )
10
10
  from typing import Any
11
11
 
12
- from . import (
13
- JWT_ACCESS_MAX_AGE, JWT_REFRESH_MAX_AGE,
14
- JWT_DEFAULT_ALGORITHM, JWT_DECODING_KEY,
15
- JWT_DB_TABLE, JWT_DB_COL_KID,
16
- JWT_DB_COL_ACCOUNT, JWT_DB_COL_ALGORITHM, JWT_DB_COL_DECODER
12
+ from .jwt_constants import (
13
+ JwtParam, JwtDbParam, _JWT_CONFIG, _JWT_DB
17
14
  )
18
15
  from .jwt_registry import JwtRegistry
19
16
 
20
- # the JWT data object
17
+ # the JWT registry
21
18
  __jwt_registry: JwtRegistry = JwtRegistry()
22
19
 
23
20
 
@@ -42,8 +39,8 @@ def jwt_verify_request(request: Request) -> Response:
42
39
  """
43
40
  Verify whether the HTTP *request* has the proper authorization, as per the JWT standard.
44
41
 
45
- :param request: the request to be verified
46
- :return: *None* if the request is valid, otherwise a *Response* object reporting the error
42
+ :param request: the *request* to be verified
43
+ :return: *None* if the *request* is valid, otherwise a *Response* reporting the error
47
44
  """
48
45
  # initialize the return variable
49
46
  result: Response | None = None
@@ -80,8 +77,8 @@ def jwt_assert_account(account_id: str) -> bool:
80
77
 
81
78
  def jwt_set_account(account_id: str,
82
79
  claims: dict[str, Any],
83
- access_max_age: int = JWT_ACCESS_MAX_AGE,
84
- refresh_max_age: int = JWT_REFRESH_MAX_AGE,
80
+ access_max_age: int = _JWT_CONFIG[JwtParam.ACCESS_MAX_AGE],
81
+ refresh_max_age: int = _JWT_CONFIG[JwtParam.REFRESH_MAX_AGE],
85
82
  grace_interval: int = None,
86
83
  logger: Logger = None) -> None:
87
84
  """
@@ -182,12 +179,15 @@ def jwt_validate_token(errors: list[str] | None,
182
179
  elif token_kid and len(token_kid) > 1 and \
183
180
  token_kid[0:1] in ["A", "R"] and token_kid[1:].isdigit():
184
181
  # token was likely issued locally
185
- where_data: dict[str, Any] = {JWT_DB_COL_KID: int(token_kid[1:])}
182
+ where_data: dict[str, Any] = {
183
+ _JWT_DB[JwtDbParam.COL_KID]: int(token_kid[1:])
184
+ }
186
185
  if account_id:
187
- where_data[JWT_DB_COL_ACCOUNT] = account_id
186
+ where_data[_JWT_DB[JwtDbParam.COL_ACCOUNT]] = account_id
188
187
  recs: list[tuple[str]] = db_select(errors=op_errors,
189
- sel_stmt=f"SELECT {JWT_DB_COL_ALGORITHM}, {JWT_DB_COL_DECODER} "
190
- f"FROM {JWT_DB_TABLE}",
188
+ sel_stmt=f"SELECT {_JWT_DB[JwtDbParam.COL_ALGORITHM]}, "
189
+ f"{_JWT_DB[JwtDbParam.COL_DECODER]} "
190
+ f"FROM {_JWT_DB[JwtDbParam.TABLE]}",
191
191
  where_data=where_data,
192
192
  logger=logger)
193
193
  if recs:
@@ -201,8 +201,8 @@ def jwt_validate_token(errors: list[str] | None,
201
201
  logger.error(msg="Token not in the database")
202
202
  op_errors.append("Invalid token")
203
203
  else:
204
- token_alg = JWT_DEFAULT_ALGORITHM
205
- token_decoder = JWT_DECODING_KEY
204
+ token_alg = _JWT_CONFIG[JwtParam.DEFAULT_ALGORITHM]
205
+ token_decoder = _JWT_CONFIG[JwtParam.DECODING_KEY]
206
206
 
207
207
  # validate the token
208
208
  if not op_errors:
@@ -282,10 +282,10 @@ def jwt_revoke_token(errors: list[str] | None,
282
282
  op_errors.append("Invalid token")
283
283
  else:
284
284
  db_delete(errors=op_errors,
285
- delete_stmt=f"DELETE FROM {JWT_DB_TABLE}",
285
+ delete_stmt=f"DELETE FROM {_JWT_DB[JwtDbParam.TABLE]}",
286
286
  where_data={
287
- JWT_DB_COL_KID: int(token_kid[1:]),
288
- JWT_DB_COL_ACCOUNT: account_id
287
+ _JWT_DB[JwtDbParam.COL_KID]: int(token_kid[1:]),
288
+ _JWT_DB[JwtDbParam.COL_ACCOUNT]: account_id
289
289
  },
290
290
  logger=logger)
291
291
  if op_errors:
@@ -456,10 +456,10 @@ def jwt_refresh_tokens(errors: list[str] | None,
456
456
  if db_conn:
457
457
  # delete current refresh token
458
458
  db_delete(errors=op_errors,
459
- delete_stmt=f"DELETE FROM {JWT_DB_TABLE}",
459
+ delete_stmt=f"DELETE FROM {_JWT_DB[JwtDbParam.TABLE]}",
460
460
  where_data={
461
- JWT_DB_COL_KID: int(token_kid[1:]),
462
- JWT_DB_COL_ACCOUNT: account_id
461
+ _JWT_DB[JwtDbParam.COL_KID]: int(token_kid[1:]),
462
+ _JWT_DB[JwtDbParam.COL_ACCOUNT]: account_id
463
463
  },
464
464
  connection=db_conn,
465
465
  committable=False,
@@ -12,11 +12,8 @@ from pypomes_db import (
12
12
  from threading import Lock
13
13
  from typing import Any
14
14
 
15
- from . import (
16
- JWT_DEFAULT_ALGORITHM, JWT_ACCOUNT_LIMIT,
17
- JWT_ENCODING_KEY, JWT_DECODING_KEY,
18
- JWT_DB_TABLE, JWT_DB_COL_KID, JWT_DB_COL_ACCOUNT,
19
- JWT_DB_COL_ALGORITHM, JWT_DB_COL_DECODER, JWT_DB_COL_TOKEN
15
+ from .jwt_constants import (
16
+ JwtParam, JwtDbParam, _JWT_CONFIG, _JWT_DB
20
17
  )
21
18
 
22
19
 
@@ -144,8 +141,8 @@ class JwtRegistry:
144
141
 
145
142
  # remove from database
146
143
  db_delete(errors=None,
147
- delete_stmt=f"DELETE FROM {JWT_DB_TABLE}",
148
- where_data={JWT_DB_COL_ACCOUNT: account_id},
144
+ delete_stmt=f"DELETE FROM {_JWT_DB[JwtDbParam.TABLE]}",
145
+ where_data={_JWT_DB[JwtDbParam.COL_ACCOUNT]: account_id},
149
146
  logger=logger)
150
147
  if logger:
151
148
  if account_data:
@@ -219,8 +216,8 @@ class JwtRegistry:
219
216
  tz=timezone.utc).isoformat()
220
217
  # may raise an exception
221
218
  return jwt.encode(payload=current_claims,
222
- key=JWT_ENCODING_KEY,
223
- algorithm=JWT_DEFAULT_ALGORITHM,
219
+ key=_JWT_CONFIG[JwtParam.ENCODING_KEY],
220
+ algorithm=_JWT_CONFIG[JwtParam.DEFAULT_ALGORITHM],
224
221
  headers={"kid": nature})
225
222
 
226
223
  def issue_tokens(self,
@@ -280,8 +277,8 @@ class JwtRegistry:
280
277
  tz=timezone.utc).isoformat()
281
278
  # may raise an exception
282
279
  refresh_token: str = jwt.encode(payload=current_claims,
283
- key=JWT_ENCODING_KEY,
284
- algorithm=JWT_DEFAULT_ALGORITHM,
280
+ key=_JWT_CONFIG[JwtParam.ENCODING_KEY],
281
+ algorithm=_JWT_CONFIG[JwtParam.DEFAULT_ALGORITHM],
285
282
  headers={"kid": "R0"})
286
283
 
287
284
  # make sure to have a database connection
@@ -296,14 +293,14 @@ class JwtRegistry:
296
293
  logger=logger)
297
294
  # issue the definitive refresh token
298
295
  refresh_token = jwt.encode(payload=current_claims,
299
- key=JWT_ENCODING_KEY,
300
- algorithm=JWT_DEFAULT_ALGORITHM,
296
+ key=_JWT_CONFIG[JwtParam.ENCODING_KEY],
297
+ algorithm=_JWT_CONFIG[JwtParam.DEFAULT_ALGORITHM],
301
298
  headers={"kid": f"R{token_id}"})
302
299
  # persist it
303
300
  db_update(errors=errors,
304
- update_stmt=f"UPDATE {JWT_DB_TABLE}",
305
- update_data={JWT_DB_COL_TOKEN: refresh_token},
306
- where_data={JWT_DB_COL_KID: token_id},
301
+ update_stmt=f"UPDATE {_JWT_DB[JwtDbParam.TABLE]}",
302
+ update_data={_JWT_DB[JwtDbParam.COL_TOKEN]: refresh_token},
303
+ where_data={_JWT_DB[JwtDbParam.COL_KID]: token_id},
307
304
  connection=curr_conn,
308
305
  committable=False,
309
306
  logger=logger)
@@ -325,8 +322,8 @@ class JwtRegistry:
325
322
  current_claims["exp"] = just_now + account_data.get("access-max-age")
326
323
  # may raise an exception
327
324
  access_token: str = jwt.encode(payload=current_claims,
328
- key=JWT_ENCODING_KEY,
329
- algorithm=JWT_DEFAULT_ALGORITHM,
325
+ key=_JWT_CONFIG[JwtParam.ENCODING_KEY],
326
+ algorithm=_JWT_CONFIG[JwtParam.DEFAULT_ALGORITHM],
330
327
  headers={"kid": f"A{token_id}"})
331
328
  # return the token data
332
329
  return {
@@ -384,9 +381,9 @@ def _jwt_persist_token(account_id: str,
384
381
  # noinspection PyTypeChecker
385
382
  recs: list[tuple[int, str, str, str]] = \
386
383
  db_select(errors=errors,
387
- sel_stmt=f"SELECT {JWT_DB_COL_KID}, {JWT_DB_COL_TOKEN} "
388
- f"FROM {JWT_DB_TABLE}",
389
- where_data={JWT_DB_COL_ACCOUNT: account_id},
384
+ sel_stmt=f"SELECT {_JWT_DB[JwtDbParam.COL_KID]}, {_JWT_DB[JwtDbParam.COL_TOKEN]} "
385
+ f"FROM {_JWT_DB[JwtDbParam.TABLE]}",
386
+ where_data={_JWT_DB[JwtDbParam.COL_ACCOUNT]: account_id},
390
387
  connection=db_conn,
391
388
  committable=False,
392
389
  logger=logger)
@@ -427,8 +424,8 @@ def _jwt_persist_token(account_id: str,
427
424
  # remove expired tokens from persistence
428
425
  if expired:
429
426
  db_delete(errors=errors,
430
- delete_stmt=f"DELETE FROM {JWT_DB_TABLE}",
431
- where_data={JWT_DB_COL_KID: expired},
427
+ delete_stmt=f"DELETE FROM {_JWT_DB[JwtDbParam.TABLE]}",
428
+ where_data={_JWT_DB[JwtDbParam.COL_KID]: expired},
432
429
  connection=db_conn,
433
430
  committable=False,
434
431
  logger=logger)
@@ -438,11 +435,11 @@ def _jwt_persist_token(account_id: str,
438
435
  logger.debug(msg=f"{len(expired)} tokens of account "
439
436
  f"'{account_id}' removed from storage")
440
437
 
441
- if 0 < JWT_ACCOUNT_LIMIT <= len(recs) - len(expired):
438
+ if 0 < _JWT_CONFIG[JwtParam.ACCOUNT_LIMIT] <= len(recs) - len(expired):
442
439
  # delete the oldest token to make way for the new one
443
440
  db_delete(errors=errors,
444
- delete_stmt=f"DELETE FROM {JWT_DB_TABLE}",
445
- where_data={JWT_DB_COL_KID: oldest_id},
441
+ delete_stmt=f"DELETE FROM {_JWT_DB[JwtDbParam.TABLE]}",
442
+ where_data={_JWT_DB[JwtDbParam.COL_KID]: oldest_id},
446
443
  connection=db_conn,
447
444
  committable=False,
448
445
  logger=logger)
@@ -453,11 +450,13 @@ def _jwt_persist_token(account_id: str,
453
450
  f"'{account_id}' removed from storage")
454
451
  # persist token
455
452
  db_insert(errors=errors,
456
- insert_stmt=f"INSERT INTO {JWT_DB_TABLE}",
457
- insert_data={JWT_DB_COL_ACCOUNT: account_id,
458
- JWT_DB_COL_TOKEN: jwt_token,
459
- JWT_DB_COL_ALGORITHM: JWT_DEFAULT_ALGORITHM,
460
- JWT_DB_COL_DECODER: urlsafe_b64encode(JWT_DECODING_KEY).decode()},
453
+ insert_stmt=f"INSERT INTO {_JWT_DB[JwtDbParam.TABLE]}",
454
+ insert_data={
455
+ _JWT_DB[JwtDbParam.COL_ACCOUNT]: account_id,
456
+ _JWT_DB[JwtDbParam.COL_TOKEN]: jwt_token,
457
+ _JWT_DB[JwtDbParam.COL_ALGORITHM]: _JWT_CONFIG[JwtParam.DEFAULT_ALGORITHM],
458
+ _JWT_DB[JwtDbParam.COL_DECODER]: urlsafe_b64encode(_JWT_CONFIG[JwtParam.DECODING_KEY]).decode()
459
+ },
461
460
  connection=db_conn,
462
461
  committable=False,
463
462
  logger=logger)
@@ -468,13 +467,13 @@ def _jwt_persist_token(account_id: str,
468
467
  # HAZARD: JWT_DB_COL_TOKEN's column type might prevent it for being used in a WHERE clause
469
468
  where_clause: str | None = None
470
469
  if existing_ids:
471
- where_clause = f"{JWT_DB_COL_KID} NOT IN {existing_ids}"
470
+ where_clause = f"{_JWT_DB[JwtDbParam.COL_KID]} NOT IN {existing_ids}"
472
471
  where_clause = where_clause.replace("[", "(", 1).replace("]", ")", 1)
473
472
  reply: list[tuple[int]] = db_select(errors=errors,
474
- sel_stmt=f"SELECT {JWT_DB_COL_KID} "
475
- f"FROM {JWT_DB_TABLE}",
473
+ sel_stmt=f"SELECT {_JWT_DB[JwtDbParam.COL_KID]} "
474
+ f"FROM {_JWT_DB[JwtDbParam.TABLE]}",
476
475
  where_clause=where_clause,
477
- where_data={JWT_DB_COL_ACCOUNT: account_id},
476
+ where_data={_JWT_DB[JwtDbParam.COL_ACCOUNT]: account_id},
478
477
  require_count=1,
479
478
  connection=db_conn,
480
479
  committable=False,
@@ -1,33 +0,0 @@
1
- from .jwt_constants import (
2
- JWT_DB_ENGINE, JWT_DB_HOST, JWT_DB_NAME,
3
- JWT_DB_PORT, JWT_DB_USER, JWT_DB_PWD,
4
- JWT_DB_TABLE, JWT_DB_COL_KID, JWT_DB_COL_ACCOUNT,
5
- JWT_DB_COL_ALGORITHM, JWT_DB_COL_DECODER, JWT_DB_COL_TOKEN,
6
- JWT_ACCOUNT_LIMIT, JWT_ENCODING_KEY, JWT_DECODING_KEY,
7
- JWT_DEFAULT_ALGORITHM, JWT_ACCESS_MAX_AGE, JWT_REFRESH_MAX_AGE
8
- )
9
- from .jwt_pomes import (
10
- jwt_needed, jwt_verify_request,
11
- jwt_assert_account, jwt_set_account, jwt_remove_account,
12
- jwt_issue_token, jwt_issue_tokens, jwt_refresh_tokens,
13
- jwt_get_claims, jwt_validate_token, jwt_revoke_token
14
- )
15
-
16
- __all__ = [
17
- # jwt_constants
18
- "JWT_DB_ENGINE", "JWT_DB_HOST", "JWT_DB_NAME",
19
- "JWT_DB_PORT", "JWT_DB_USER", "JWT_DB_PWD",
20
- "JWT_DB_TABLE", "JWT_DB_COL_KID", "JWT_DB_COL_ACCOUNT",
21
- "JWT_DB_COL_ALGORITHM", "JWT_DB_COL_DECODER", "JWT_DB_COL_TOKEN",
22
- "JWT_ACCOUNT_LIMIT", "JWT_ENCODING_KEY", "JWT_DECODING_KEY",
23
- "JWT_DEFAULT_ALGORITHM", "JWT_ACCESS_MAX_AGE", "JWT_REFRESH_MAX_AGE",
24
- # jwt_pomes
25
- "jwt_needed", "jwt_verify_request",
26
- "jwt_assert_account", "jwt_set_account", "jwt_remove_account",
27
- "jwt_issue_token", "jwt_issue_tokens", "jwt_refresh_tokens",
28
- "jwt_get_claims", "jwt_validate_token", "jwt_revoke_token"
29
- ]
30
-
31
- from importlib.metadata import version
32
- __version__ = version("pypomes_jwt")
33
- __version_info__ = tuple(int(i) for i in __version__.split(".") if i.isdigit())
@@ -1,71 +0,0 @@
1
- from cryptography.hazmat.primitives import serialization
2
- from cryptography.hazmat.primitives.asymmetric import rsa
3
- from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey, RSAPublicKey
4
- from pypomes_core import (
5
- APP_PREFIX,
6
- env_get_str, env_get_bytes, env_get_int
7
- )
8
- from pypomes_db import DbEngine, db_setup
9
- from secrets import token_bytes
10
- from sys import stderr
11
- from typing import Final
12
-
13
- # database specs for token persistence
14
- JWT_DB_HOST: Final[str] = env_get_str(key=f"{APP_PREFIX}_JWT_DB_HOST")
15
- JWT_DB_NAME: Final[str] = env_get_str(key=f"{APP_PREFIX}_JWT_DB_NAME")
16
- JWT_DB_PORT: Final[int] = env_get_int(key=f"{APP_PREFIX}_JWT_DB_PORT")
17
- JWT_DB_USER: Final[str] = env_get_str(key=f"{APP_PREFIX}_JWT_DB_USER")
18
- JWT_DB_PWD: Final[str] = env_get_str(key=f"{APP_PREFIX}_JWT_DB_PWD")
19
- JWT_DB_CLIENT: Final[str] = env_get_str(key=f"{APP_PREFIX}_JWT_DB_CLIENT") # for Oracle, only
20
- JWT_DB_DRIVER: Final[str] = env_get_str(key=f"{APP_PREFIX}_JWT_DB_DRIVER") # for SQLServer, only
21
- JWT_DB_TABLE: Final[str] = env_get_str(key=f"{APP_PREFIX}_JWT_DB_TABLE")
22
- JWT_DB_COL_ACCOUNT: Final[str] = env_get_str(key=f"{APP_PREFIX}_JWT_DB_COL_ACCOUNT")
23
- JWT_DB_COL_ALGORITHM: Final[str] = env_get_str(key=f"{APP_PREFIX}_JWT_DB_COL_ALGORITHM")
24
- JWT_DB_COL_DECODER: Final[str] = env_get_str(key=f"{APP_PREFIX}_JWT_DB_COL_DECODER")
25
- JWT_DB_COL_KID: Final[str] = env_get_str(key=f"{APP_PREFIX}_JWT_DB_COL_KID")
26
- JWT_DB_COL_TOKEN: Final[str] = env_get_str(key=f"{APP_PREFIX}_JWT_DB_COL_TOKEN")
27
-
28
- # define and validate the database engine
29
- JWT_DB_ENGINE: Final[DbEngine] = DbEngine(env_get_str(key=f"{APP_PREFIX}_JWT_DB_ENGINE"))
30
- if not db_setup(engine=JWT_DB_ENGINE,
31
- db_name=JWT_DB_NAME,
32
- db_user=JWT_DB_USER,
33
- db_pwd=JWT_DB_PWD,
34
- db_host=JWT_DB_HOST,
35
- db_port=JWT_DB_PORT,
36
- db_client=JWT_DB_CLIENT,
37
- db_driver=JWT_DB_DRIVER):
38
- stderr.write("Invalid database parameters\n")
39
-
40
- # one of HS256, HS512, RS256, RS512
41
- JWT_DEFAULT_ALGORITHM: Final[str] = env_get_str(key=f"{APP_PREFIX}_JWT_DEFAULT_ALGORITHM",
42
- def_value="RS256")
43
- # recommended: between 5 min and 1 hour (set to 5 min)
44
- JWT_ACCESS_MAX_AGE: Final[int] = env_get_int(key=f"{APP_PREFIX}_JWT_ACCESS_MAX_AGE",
45
- def_value=300)
46
- # recommended: at least 2 hours (set to 24 hours)
47
- JWT_REFRESH_MAX_AGE: Final[int] = env_get_int(key=f"{APP_PREFIX}_JWT_REFRESH_MAX_AGE",
48
- def_value=86400)
49
- JWT_ACCOUNT_LIMIT: Final[int] = env_get_int(key=f"{APP_PREFIX}_JWT_ACCOUNT_LIMIT")
50
-
51
- # recommended: allow the encode and decode keys to be generated anew when app starts
52
- __encoding_key: bytes = env_get_bytes(key=f"{APP_PREFIX}_JWT_ENCODING_KEY",
53
- encoding="base64url")
54
- __decoding_key: bytes
55
- if JWT_DEFAULT_ALGORITHM in ["HS256", "HS512"]:
56
- if not __encoding_key:
57
- __encoding_key = token_bytes(nbytes=32)
58
- __decoding_key = __encoding_key
59
- else:
60
- __decoding_key: bytes = env_get_bytes(key=f"{APP_PREFIX}_JWT_DECODING_KEY")
61
- if not __encoding_key or not __decoding_key:
62
- __priv_key: RSAPrivateKey = rsa.generate_private_key(public_exponent=65537,
63
- key_size=2048)
64
- __encoding_key = __priv_key.private_bytes(encoding=serialization.Encoding.PEM,
65
- format=serialization.PrivateFormat.PKCS8,
66
- encryption_algorithm=serialization.NoEncryption())
67
- __pub_key: RSAPublicKey = __priv_key.public_key()
68
- __decoding_key = __pub_key.public_bytes(encoding=serialization.Encoding.PEM,
69
- format=serialization.PublicFormat.SubjectPublicKeyInfo)
70
- JWT_ENCODING_KEY: Final[bytes] = __encoding_key
71
- JWT_DECODING_KEY: Final[bytes] = __decoding_key
File without changes
File without changes
File without changes