pypomes-jwt 0.9.8__tar.gz → 0.9.9__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: 0.9.8
3
+ Version: 0.9.9
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.8.5
16
- Requires-Dist: pypomes-db>=1.9.7
15
+ Requires-Dist: pypomes-core>=1.8.6
16
+ Requires-Dist: pypomes-db>=1.9.8
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
6
6
 
7
7
  [project]
8
8
  name = "pypomes_jwt"
9
- version = "0.9.8"
9
+ version = "0.9.9"
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.8.5",
25
- "pypomes_db>=1.9.7"
24
+ "pypomes_core>=1.8.6",
25
+ "pypomes_db>=1.9.8"
26
26
  ]
27
27
 
28
28
  [project.urls]
@@ -161,69 +161,76 @@ def jwt_validate_token(errors: list[str] | None,
161
161
  """
162
162
  # initialize the return variable
163
163
  result: dict[str, Any] | None = None
164
+
164
165
  if logger:
165
166
  logger.debug(msg="Validate JWT token")
167
+ op_errors: list[str] = []
166
168
 
167
169
  # extract needed data from token header
168
- token_header: dict[str, Any] = jwt.get_unverified_header(jwt=token)
169
- token_kid: str = token_header.get("kid")
170
- token_alg: str | None = None
171
- token_decoder: bytes | None = None
172
- op_errors: list[str] = []
170
+ token_header: dict[str, Any] | None = None
171
+ try:
172
+ token_header: dict[str, Any] = jwt.get_unverified_header(jwt=token)
173
+ except Exception as e:
174
+ op_errors.append(str(e))
173
175
 
174
- # retrieve token data from database
175
- if nature and not (token_kid and token_kid[0:1] == nature):
176
- op_errors.append("Invalid token")
177
- elif token_kid and len(token_kid) > 1 and \
178
- token_kid[0:1] in ["A", "R"] and token[1:].isdigit():
179
- # token was likely issued locally
180
- where_data: dict[str, Any] = {JWT_DB_COL_KID: int(token_kid[1:])}
181
- if account_id:
182
- where_data[JWT_DB_COL_ACCOUNT] = account_id
183
- recs: list[tuple[str]] = db_select(errors=op_errors,
184
- sel_stmt=f"SELECT {JWT_DB_COL_ALGORITHM}, {JWT_DB_COL_DECODER} "
185
- f"FROM {JWT_DB_TABLE}",
186
- where_data=where_data,
187
- logger=logger)
188
- if recs:
189
- token_alg = recs[0][0]
190
- token_decoder = urlsafe_b64decode(recs[0][1])
191
- else:
176
+ if not op_errors:
177
+ token_kid: str = token_header.get("kid")
178
+ token_alg: str | None = None
179
+ token_decoder: bytes | None = None
180
+
181
+ # retrieve token data from database
182
+ if nature and not (token_kid and token_kid[0:1] == nature):
192
183
  op_errors.append("Invalid token")
193
- else:
194
- token_alg = JWT_DEFAULT_ALGORITHM
195
- token_decoder = JWT_DECODING_KEY
184
+ elif token_kid and len(token_kid) > 1 and \
185
+ token_kid[0:1] in ["A", "R"] and token_kid[1:].isdigit():
186
+ # token was likely issued locally
187
+ where_data: dict[str, Any] = {JWT_DB_COL_KID: int(token_kid[1:])}
188
+ if account_id:
189
+ where_data[JWT_DB_COL_ACCOUNT] = account_id
190
+ recs: list[tuple[str]] = db_select(errors=op_errors,
191
+ sel_stmt=f"SELECT {JWT_DB_COL_ALGORITHM}, {JWT_DB_COL_DECODER} "
192
+ f"FROM {JWT_DB_TABLE}",
193
+ where_data=where_data,
194
+ logger=logger)
195
+ if recs:
196
+ token_alg = recs[0][0]
197
+ token_decoder = urlsafe_b64decode(recs[0][1])
198
+ else:
199
+ op_errors.append("Invalid token")
200
+ else:
201
+ token_alg = JWT_DEFAULT_ALGORITHM
202
+ token_decoder = JWT_DECODING_KEY
196
203
 
197
204
  # validate the token
198
- if not op_errors:
199
- try:
200
- # raises:
201
- # InvalidTokenError: token is invalid
202
- # InvalidKeyError: authentication key is not in the proper format
203
- # ExpiredSignatureError: token and refresh period have expired
204
- # InvalidSignatureError: signature does not match the one provided as part of the token
205
- # ImmatureSignatureError: 'nbf' or 'iat' claim represents a timestamp in the future
206
- # InvalidAlgorithmError: the specified algorithm is not recognized
207
- # InvalidIssuedAtError: 'iat' claim is non-numeric
208
- # MissingRequiredClaimError: a required claim is not contained in the claimset
209
- payload: dict[str, Any] = jwt.decode(jwt=token,
210
- options={
211
- "verify_signature": True,
212
- "verify_exp": True,
213
- "verify_nbf": True
214
- },
215
- key=token_decoder,
216
- require=["iat", "iss", "exp", "sub"],
217
- algorithms=token_alg)
218
- if account_id and payload.get("sub") != account_id:
219
- op_errors.append("Token does not belong to account")
220
- else:
221
- result = {
222
- "header": token_header,
223
- "payload": payload
224
- }
225
- except Exception as e:
226
- op_errors.append(str(e))
205
+ if not op_errors:
206
+ try:
207
+ # raises:
208
+ # InvalidTokenError: token is invalid
209
+ # InvalidKeyError: authentication key is not in the proper format
210
+ # ExpiredSignatureError: token and refresh period have expired
211
+ # InvalidSignatureError: signature does not match the one provided as part of the token
212
+ # ImmatureSignatureError: 'nbf' or 'iat' claim represents a timestamp in the future
213
+ # InvalidAlgorithmError: the specified algorithm is not recognized
214
+ # InvalidIssuedAtError: 'iat' claim is non-numeric
215
+ # MissingRequiredClaimError: a required claim is not contained in the claimset
216
+ payload: dict[str, Any] = jwt.decode(jwt=token,
217
+ options={
218
+ "verify_signature": True,
219
+ "verify_exp": True,
220
+ "verify_nbf": True
221
+ },
222
+ key=token_decoder,
223
+ require=["iat", "iss", "exp", "sub"],
224
+ algorithms=token_alg)
225
+ if account_id and payload.get("sub") != account_id:
226
+ op_errors.append("Token does not belong to account")
227
+ else:
228
+ result = {
229
+ "header": token_header,
230
+ "payload": payload
231
+ }
232
+ except Exception as e:
233
+ op_errors.append(str(e))
227
234
 
228
235
  if op_errors:
229
236
  err_msg: str = "; ".join(op_errors)
@@ -256,7 +263,7 @@ def jwt_revoke_token(errors: list[str] | None,
256
263
  result: bool = False
257
264
 
258
265
  if logger:
259
- logger.debug(msg=f"Revoking refresh token of '{account_id}'")
266
+ logger.debug(msg=f"Revoking token of account '{account_id}'")
260
267
 
261
268
  op_errors: list[str] = []
262
269
  token_claims: dict[str, Any] = jwt_validate_token(errors=op_errors,
@@ -379,12 +379,13 @@ def _jwt_persist_token(errors: list[str],
379
379
  if logger:
380
380
  logger.debug(msg=f"Read {len(recs)} token from storage for account '{account_id}'")
381
381
  # remove the expired tokens
382
- oldest: int = sys.maxsize
383
- surplus: int | None = None
382
+ just_now: int = int(datetime.now(tz=timezone.utc).timestamp())
383
+ oldest_ts: int = sys.maxsize
384
+ oldest_id: int | None = None
384
385
  expired: list[int] = []
385
386
  for rec in recs:
386
387
  token: str = rec[1]
387
- token_kid: int = rec[0]
388
+ token_id: int = rec[0]
388
389
  token_claims: dict[str, Any] = jwt_get_claims(errors=errors,
389
390
  token=token,
390
391
  logger=logger)
@@ -393,14 +394,14 @@ def _jwt_persist_token(errors: list[str],
393
394
 
394
395
  # find expired tokens
395
396
  exp: int = token_claims["payload"].get("exp", sys.maxsize)
396
- if exp < datetime.now(tz=timezone.utc).timestamp():
397
- expired.append(token_kid)
397
+ if exp < just_now:
398
+ expired.append(token_id)
398
399
 
399
400
  # find oldest token
400
401
  iat: int = token_claims["payload"].get("iat", sys.maxsize)
401
- if iat < oldest:
402
- oldest = exp
403
- surplus = token_kid
402
+ if iat < oldest_ts:
403
+ oldest_ts = exp
404
+ oldest_id = token_id
404
405
 
405
406
  # remove expired tokens from persistence
406
407
  if expired:
@@ -419,7 +420,7 @@ def _jwt_persist_token(errors: list[str],
419
420
  # delete the oldest token to make way for the new one
420
421
  db_delete(errors=errors,
421
422
  delete_stmt=f"DELETE FROM {JWT_DB_TABLE}",
422
- where_data={JWT_DB_COL_KID: surplus},
423
+ where_data={JWT_DB_COL_KID: oldest_id},
423
424
  connection=db_conn,
424
425
  logger=logger)
425
426
  if errors:
File without changes
File without changes
File without changes