pypomes-jwt 0.7.8__py3-none-any.whl → 0.8.0__py3-none-any.whl

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/__init__.py CHANGED
@@ -8,7 +8,7 @@ from .jwt_constants import (
8
8
  from .jwt_pomes import (
9
9
  jwt_needed, jwt_verify_request,
10
10
  jwt_get_tokens, jwt_get_claims, jwt_validate_token,
11
- jwt_assert_access, jwt_set_access, jwt_remove_access, jwt_revoke_tokens
11
+ jwt_assert_account, jwt_set_account, jwt_remove_account, jwt_revoke_token
12
12
  )
13
13
 
14
14
  __all__ = [
@@ -21,7 +21,7 @@ __all__ = [
21
21
  # jwt_pomes
22
22
  "jwt_needed", "jwt_verify_request",
23
23
  "jwt_get_tokens", "jwt_get_claims", "jwt_validate_token",
24
- "jwt_assert_access", "jwt_set_access", "jwt_remove_access", "jwt_revoke_tokens"
24
+ "jwt_assert_account", "jwt_set_account", "jwt_remove_account", "jwt_revoke_token"
25
25
  ]
26
26
 
27
27
  from importlib.metadata import version
@@ -3,7 +3,7 @@ from cryptography.hazmat.primitives.asymmetric import rsa
3
3
  from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey, RSAPublicKey
4
4
  from pypomes_core import (
5
5
  APP_PREFIX,
6
- env_get_str, env_get_bytes, env_get_int, env_get_bool
6
+ env_get_str, env_get_bytes, env_get_int
7
7
  )
8
8
  from secrets import token_bytes
9
9
  from typing import Final
@@ -54,8 +54,6 @@ JWT_ACCESS_MAX_AGE: Final[int] = env_get_int(key=f"{APP_PREFIX}_JWT_ACCESS_MAX_A
54
54
  # recommended: at least 2 hours (set to 24 hours)
55
55
  JWT_REFRESH_MAX_AGE: Final[int] = env_get_int(key=f"{APP_PREFIX}_JWT_REFRESH_MAX_AGE",
56
56
  def_value=86400)
57
- JWT_ROTATE_TOKENS: Final[bool] = env_get_bool(key=f"{APP_PREFIX}_JWT_ROTATE_TOKENS",
58
- def_value=True)
59
57
  JWT_ACCOUNT_LIMIT: Final[int] = env_get_int(key=f"{APP_PREFIX}_JWT_ACCOUNT_LIMIT")
60
58
 
61
59
  # recommended: allow the encode and decode keys to be generated anew when app starts
pypomes_jwt/jwt_data.py CHANGED
@@ -10,9 +10,8 @@ from threading import Lock
10
10
  from typing import Any
11
11
 
12
12
  from .jwt_constants import (
13
- JWT_DEFAULT_ALGORITHM, JWT_ENCODING_KEY, JWT_DECODING_KEY,
14
- JWT_ROTATE_TOKENS, JWT_ACCOUNT_LIMIT, JWT_DB_ENGINE,
15
- JWT_DB_TABLE, JWT_DB_COL_ACCOUNT, JWT_DB_COL_HASH, JWT_DB_COL_TOKEN
13
+ JWT_DEFAULT_ALGORITHM, JWT_ENCODING_KEY, JWT_DECODING_KEY, JWT_ACCOUNT_LIMIT,
14
+ JWT_DB_ENGINE, JWT_DB_TABLE, JWT_DB_COL_ACCOUNT, JWT_DB_COL_HASH, JWT_DB_COL_TOKEN
16
15
  )
17
16
 
18
17
 
@@ -21,30 +20,30 @@ class JwtData:
21
20
  Shared JWT data for security token access.
22
21
 
23
22
  Instance variables:
24
- - access_lock: lock for safe multi-threading access
25
- - access_data: dictionary holding the JWT token data, organized by account id:
26
- {
27
- <account-id>: {
28
- "reference-url": # the reference URL
29
- "remote-provider": <bool>, # whether the JWT provider is a remote server
30
- "request-timeout": <int>, # in seconds - defaults to no timeout
31
- "access-max-age": <int>, # in seconds - defaults to JWT_ACCESS_MAX_AGE
32
- "refresh-max-age": <int>, # in seconds - defaults to JWT_REFRESH_MAX_AGE
33
- "grace-interval": <int> # time to wait for token to be valid, in seconds
34
- "token-audience": <string> # the audience the token is intended for
35
- "token_nonce": <string> # value used to associate a client session with a token
36
- "claims": {
37
- "birthdate": <string>, # subject's birth date
38
- "email": <string>, # subject's email
39
- "gender": <string>, # subject's gender
40
- "name": <string>, # subject's name
41
- "roles": <List[str]>, # subject roles
42
- "nonce": <string>, # value used to associate a Client session with a token
43
- ...
44
- }
45
- },
46
- ...
47
- }
23
+ - access_lock: lock for safe multi-threading access
24
+ - access_data: dictionary holding the JWT token data, organized by account id:
25
+ {
26
+ <account-id>: {
27
+ "reference-url": # the reference URL
28
+ "remote-provider": <bool>, # whether the JWT provider is a remote server
29
+ "request-timeout": <int>, # in seconds - defaults to no timeout
30
+ "access-max-age": <int>, # in seconds - defaults to JWT_ACCESS_MAX_AGE
31
+ "refresh-max-age": <int>, # in seconds - defaults to JWT_REFRESH_MAX_AGE
32
+ "grace-interval": <int> # time to wait for token to be valid, in seconds
33
+ "token-audience": <string> # the audience the token is intended for
34
+ "token_nonce": <string> # value used to associate a client session with a token
35
+ "claims": {
36
+ "birthdate": <string>, # subject's birth date
37
+ "email": <string>, # subject's email
38
+ "gender": <string>, # subject's gender
39
+ "name": <string>, # subject's name
40
+ "roles": <List[str]>, # subject roles
41
+ "nonce": <string>, # value used to associate a Client session with a token
42
+ ...
43
+ }
44
+ },
45
+ ...
46
+ }
48
47
 
49
48
  JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between
50
49
  two parties. It is fully described in the RFC 7519, issued by the Internet Engineering Task Force
@@ -53,24 +52,30 @@ class JwtData:
53
52
  as token-related and account-related. All times are UTC.
54
53
 
55
54
  Token-related claims are mostly required claims, and convey information about the token itself:
56
- "exp": <timestamp> # expiration time
57
- "iat": <timestamp> # issued at
58
- "iss": <string> # issuer (for remote providers, URL to obtain and validate the access tokens)
59
- "jti": <string> # JWT id
60
- "sub": <string> # subject (the account identification)
61
- "nat": <string> # nature of token (A: access; R: refresh) - locally issued tokens, only
62
- # optional:
63
- "aud": <string> # token audience
64
- "nbt": <timestamp> # not before time
55
+ "exp": <timestamp> # expiration time
56
+ "iat": <timestamp> # issued at
57
+ "iss": <string> # issuer (for remote providers, URL to obtain and validate the access tokens)
58
+ "jti": <string> # JWT id
59
+ "sub": <string> # subject (the account identification)
60
+ "nat": <string> # nature of token (A: access; R: refresh) - locally issued tokens, only
61
+ # optional:
62
+ "aud": <string> # token audience
63
+ "nbt": <timestamp> # not before time
65
64
 
66
65
  Account-related claims are optional claims, and convey information about the registered account they belong to.
67
66
  Alhough they can be freely specified, these are some of the most commonly used claims:
68
- "birthdate": <string> # subject's birth date
69
- "email": <string> # subject's email
70
- "gender": <string> # subject's gender
71
- "name": <string> # subject's name
72
- "roles": <List[str]> # subject roles
73
- "nonce": <string> # value used to associate a client session with a token
67
+ "birthdate": <string> # subject's birth date
68
+ "email": <string> # subject's email
69
+ "gender": <string> # subject's gender
70
+ "name": <string> # subject's name
71
+ "roles": <List[str]> # subject roles
72
+ "nonce": <string> # value used to associate a client session with a token
73
+
74
+ The token header has these items:
75
+ "alg": <string> # the algorithm used to sign the token (one of 'HS256', 'HS512', 'RSA256', 'RSA512')
76
+ "typ": <string> # the token type (fixed to 'JWT'
77
+ "kid": <string> # a reference to the encoding/decoding keys used
78
+ # (if issued by the local server, holds the public key, if assimetric keys were used)
74
79
  """
75
80
  def __init__(self) -> None:
76
81
  """
@@ -79,18 +84,18 @@ class JwtData:
79
84
  self.access_lock: Lock = Lock()
80
85
  self.access_data: dict[str, Any] = {}
81
86
 
82
- def add_access(self,
83
- account_id: str,
84
- reference_url: str,
85
- claims: dict[str, Any],
86
- access_max_age: int,
87
- refresh_max_age: int,
88
- grace_interval: int,
89
- token_audience: str,
90
- token_nonce: str,
91
- request_timeout: int,
92
- remote_provider: bool,
93
- logger: Logger = None) -> None:
87
+ def add_account(self,
88
+ account_id: str,
89
+ reference_url: str,
90
+ claims: dict[str, Any],
91
+ access_max_age: int,
92
+ refresh_max_age: int,
93
+ grace_interval: int,
94
+ token_audience: str,
95
+ token_nonce: str,
96
+ request_timeout: int,
97
+ remote_provider: bool,
98
+ logger: Logger = None) -> None:
94
99
  """
95
100
  Add to storage the parameters needed to produce and validate JWT tokens for *account_id*.
96
101
 
@@ -130,9 +135,9 @@ class JwtData:
130
135
  elif logger:
131
136
  logger.warning(f"JWT data already exists for '{account_id}'")
132
137
 
133
- def remove_access(self,
134
- account_id: str,
135
- logger: Logger) -> bool:
138
+ def remove_account(self,
139
+ account_id: str,
140
+ logger: Logger) -> bool:
136
141
  """
137
142
  Remove from storage the access data for *account_id*.
138
143
 
@@ -144,6 +149,12 @@ class JwtData:
144
149
  with self.access_lock:
145
150
  account_data = self.access_data.pop(account_id, None)
146
151
 
152
+ if account_data and JWT_DB_ENGINE:
153
+ from pypomes_db import db_delete
154
+ db_delete(errors=None,
155
+ delete_stmt=f"DELETE FROM {JWT_DB_TABLE}",
156
+ where_data={JWT_DB_COL_ACCOUNT: account_id},
157
+ logger=logger)
147
158
  if logger:
148
159
  if account_data:
149
160
  logger.debug(f"Removed JWT data for '{account_id}'")
@@ -227,55 +238,36 @@ class JwtData:
227
238
  # JWT service is being provided locally
228
239
  just_now: int = int(datetime.now(tz=timezone.utc).timestamp())
229
240
  current_claims["iat"] = just_now
230
- if JWT_DEFAULT_ALGORITHM in []:
231
- current_claims["kid"] = JWT_DECODING_KEY
241
+ token_header: dict[str, Any] = None \
242
+ if JWT_DEFAULT_ALGORITHM not in ["RSA256", "RSA512"] \
243
+ else {"kid": JWT_DECODING_KEY}
232
244
 
233
- # retrieve the refresh token associated with the account id
234
- refresh_token: str | None = None
235
- if JWT_DB_ENGINE:
236
- from pypomes_db import db_select, db_delete
237
- if JWT_ROTATE_TOKENS:
238
- db_delete(errors=errors,
239
- delete_stmt=f"DELETE FROM {JWT_DB_TABLE} "
240
- f"WHERE {JWT_DB_COL_ACCOUNT} = '{account_id}'",
241
- logger=logger)
242
- else:
243
- recs: list[tuple[str]] = \
244
- db_select(errors=errors,
245
- sel_stmt=f"SELECT token FROM {JWT_DB_TABLE} "
246
- f"WHERE {JWT_DB_COL_ACCOUNT} = '{account_id}'",
247
- max_count=1,
248
- logger=logger)
249
- if recs:
250
- refresh_token = recs[0][0]
251
- if errors:
252
- raise RuntimeError(" - ".join(errors))
253
-
254
- # was it obtained ?
255
- if not refresh_token:
256
- # no, issue a new one
257
- current_claims["exp"] = just_now + account_data.get("refresh-max-age")
258
- current_claims["nat"] = "R"
259
- # may raise an exception
260
- refresh_token: str = jwt.encode(payload=current_claims,
261
- key=JWT_ENCODING_KEY,
262
- algorithm=JWT_DEFAULT_ALGORITHM)
263
- # persist the new refresh token
264
- if JWT_DB_ENGINE:
265
- # persist the refresh token
266
- _jwt_persist_token(account_id=account_id,
267
- jwt_token=refresh_token,
268
- logger=logger)
269
- if errors:
270
- raise RuntimeError(" - ".join(errors))
271
-
272
- # issue the access token
245
+ # issue the access token first
273
246
  current_claims["nat"] = "A"
274
247
  current_claims["exp"] = just_now + account_data.get("access-max-age")
275
248
  # may raise an exception
276
249
  access_token: str = jwt.encode(payload=current_claims,
277
250
  key=JWT_ENCODING_KEY,
278
- algorithm=JWT_DEFAULT_ALGORITHM)
251
+ algorithm=JWT_DEFAULT_ALGORITHM,
252
+ headers=token_header)
253
+
254
+ # then issue the refresh token
255
+ current_claims["exp"] = just_now + account_data.get("refresh-max-age")
256
+ current_claims["nat"] = "R"
257
+ # may raise an exception
258
+ refresh_token: str = jwt.encode(payload=current_claims,
259
+ key=JWT_ENCODING_KEY,
260
+ algorithm=JWT_DEFAULT_ALGORITHM,
261
+ headers=token_header)
262
+ if JWT_DB_ENGINE:
263
+ # persist the refresh token
264
+ _jwt_persist_token(errors=errors,
265
+ account_id=account_id,
266
+ jwt_token=refresh_token,
267
+ logger=logger)
268
+ if errors:
269
+ raise RuntimeError("; ".join(errors))
270
+
279
271
  # return the token data
280
272
  result = {
281
273
  "access_token": access_token,
@@ -304,7 +296,9 @@ def _jwt_request_token(errors: list[str],
304
296
  Expected structure of the return data:
305
297
  {
306
298
  "access_token": <jwt-token>,
307
- "expires_in": <seconds-to-expiration>
299
+ "created_in": <timestamp>,
300
+ "expires_in": <seconds-to-expiration>,
301
+ "refresh_token": <token>
308
302
  }
309
303
  It is up to the invoker to make sure that the *claims* data conform to the requirements
310
304
  of the provider issuing the JWT token.
@@ -345,24 +339,25 @@ def _jwt_request_token(errors: list[str],
345
339
  return result
346
340
 
347
341
 
348
- def _jwt_persist_token(account_id: str,
342
+ def _jwt_persist_token(errors: list[str],
343
+ account_id: str,
349
344
  jwt_token: str,
350
- logger: Logger = None) -> bool:
345
+ logger: Logger = None) -> None:
351
346
  """
352
347
  Persist the given token, making sure that the account limit is adhered to.
353
348
 
354
- :param jwt_token: the JWT token to persist
349
+ :param errors: incidental errors
355
350
  :param account_id: the account identification
356
- :returns: *True* if token was persisted, *False* otherwise
351
+ :param jwt_token: the JWT token to persist
352
+ :param logger: optional logger
357
353
  """
358
354
  from pypomes_db import db_select, db_insert, db_delete
359
355
  from .jwt_pomes import jwt_get_claims
360
356
 
361
357
  # retrieve the account's tokens
362
- errors: list[str] = []
363
358
  recs: list[tuple[str]] = db_select(errors=errors,
364
359
  sel_stmt=f"SELECT {JWT_DB_COL_HASH}, {JWT_DB_COL_TOKEN} FROM {JWT_DB_TABLE} ",
365
- where_data={JWT_DB_COL_ACCOUNT: f"'{account_id}'"})
360
+ where_data={JWT_DB_COL_ACCOUNT: account_id})
366
361
  if not errors:
367
362
  if logger:
368
363
  logger.debug(msg=f"Read {len(recs)} token from storage for account '{account_id}'")
@@ -371,14 +366,12 @@ def _jwt_persist_token(account_id: str,
371
366
  for rec in recs:
372
367
  token: str = rec[1]
373
368
  token_hash: str = rec[0]
374
- op_errors: list[str] = []
375
- token_claims: dict[str, Any] = jwt_get_claims(errors=op_errors,
369
+ token_claims: dict[str, Any] = jwt_get_claims(errors=errors,
376
370
  token=token,
377
371
  validate=False,
378
372
  logger=logger)
379
- if op_errors:
373
+ if errors:
380
374
  break
381
-
382
375
  exp: int = token_claims["payload"]["exp"]
383
376
  if exp < datetime.now(tz=timezone.utc).timestamp():
384
377
  expired.append(token_hash)
@@ -390,7 +383,7 @@ def _jwt_persist_token(account_id: str,
390
383
  if db_delete(errors=errors,
391
384
  delete_stmt=f"DELETE FROM {JWT_DB_TABLE}",
392
385
  where_data={
393
- JWT_DB_COL_ACCOUNT: f"'{account_id}'",
386
+ JWT_DB_COL_ACCOUNT: account_id,
394
387
  JWT_DB_COL_HASH: expired
395
388
  },
396
389
  logger=logger) is not None:
@@ -402,12 +395,10 @@ def _jwt_persist_token(account_id: str,
402
395
  # persist token
403
396
  if not errors:
404
397
  # ruff: noqa: S324
405
- hasher = hashlib.new(name="md5")
406
- hasher.update(jwt_token.encode())
398
+ hasher = hashlib.new(name="md5",
399
+ data=jwt_token.encode())
407
400
  token_hash: str = hasher.digest().decode()
408
401
  db_insert(errors=errors,
409
402
  insert_stmt=f"INSERT INTO {JWT_DB_TABLE}",
410
403
  insert_data={"ds_hash": token_hash,
411
404
  "ds_token": jwt_token})
412
-
413
- return len(errors) == 0
pypomes_jwt/jwt_pomes.py CHANGED
@@ -1,3 +1,4 @@
1
+ import hashlib
1
2
  import jwt
2
3
  from flask import Request, Response, request
3
4
  from logging import Logger
@@ -76,7 +77,7 @@ def jwt_verify_request(request: Request,
76
77
  return result
77
78
 
78
79
 
79
- def jwt_assert_access(account_id: str) -> bool:
80
+ def jwt_assert_account(account_id: str) -> bool:
80
81
  """
81
82
  Determine whether access for *account_id* has been established.
82
83
 
@@ -86,17 +87,17 @@ def jwt_assert_access(account_id: str) -> bool:
86
87
  return __jwt_data.access_data.get(account_id) is not None
87
88
 
88
89
 
89
- def jwt_set_access(account_id: str,
90
- reference_url: str,
91
- claims: dict[str, Any],
92
- access_max_age: int = JWT_ACCESS_MAX_AGE,
93
- refresh_max_age: int = JWT_REFRESH_MAX_AGE,
94
- grace_interval: int = None,
95
- token_audience: str = None,
96
- token_nonce: str = None,
97
- request_timeout: int = None,
98
- remote_provider: bool = True,
99
- logger: Logger = None) -> None:
90
+ def jwt_set_account(account_id: str,
91
+ reference_url: str,
92
+ claims: dict[str, Any],
93
+ access_max_age: int = JWT_ACCESS_MAX_AGE,
94
+ refresh_max_age: int = JWT_REFRESH_MAX_AGE,
95
+ grace_interval: int = None,
96
+ token_audience: str = None,
97
+ token_nonce: str = None,
98
+ request_timeout: int = None,
99
+ remote_provider: bool = True,
100
+ logger: Logger = None) -> None:
100
101
  """
101
102
  Set the data needed to obtain JWT tokens for *account_id*.
102
103
 
@@ -113,7 +114,7 @@ def jwt_set_access(account_id: str,
113
114
  :param logger: optional logger
114
115
  """
115
116
  if logger:
116
- logger.debug(msg=f"Register access data for '{account_id}'")
117
+ logger.debug(msg=f"Register account data for '{account_id}'")
117
118
 
118
119
  # extract the claims provided in the reference URL's query string
119
120
  pos: int = reference_url.find("?")
@@ -124,21 +125,21 @@ def jwt_set_access(account_id: str,
124
125
  reference_url = reference_url[:pos]
125
126
 
126
127
  # register the JWT service
127
- __jwt_data.add_access(account_id=account_id,
128
- reference_url=reference_url,
129
- claims=claims,
130
- access_max_age=access_max_age,
131
- refresh_max_age=refresh_max_age,
132
- grace_interval=grace_interval,
133
- token_audience=token_audience,
134
- token_nonce=token_nonce,
135
- request_timeout=request_timeout,
136
- remote_provider=remote_provider,
137
- logger=logger)
138
-
139
-
140
- def jwt_remove_access(account_id: str,
141
- logger: Logger = None) -> bool:
128
+ __jwt_data.add_account(account_id=account_id,
129
+ reference_url=reference_url,
130
+ claims=claims,
131
+ access_max_age=access_max_age,
132
+ refresh_max_age=refresh_max_age,
133
+ grace_interval=grace_interval,
134
+ token_audience=token_audience,
135
+ token_nonce=token_nonce,
136
+ request_timeout=request_timeout,
137
+ remote_provider=remote_provider,
138
+ logger=logger)
139
+
140
+
141
+ def jwt_remove_account(account_id: str,
142
+ logger: Logger = None) -> bool:
142
143
  """
143
144
  Remove from storage the JWT access data for *account_id*.
144
145
 
@@ -149,8 +150,8 @@ def jwt_remove_access(account_id: str,
149
150
  if logger:
150
151
  logger.debug(msg=f"Remove access data for '{account_id}'")
151
152
 
152
- return __jwt_data.remove_access(account_id=account_id,
153
- logger=logger)
153
+ return __jwt_data.remove_account(account_id=account_id,
154
+ logger=logger)
154
155
 
155
156
 
156
157
  def jwt_validate_token(errors: list[str] | None,
@@ -181,7 +182,7 @@ def jwt_validate_token(errors: list[str] | None,
181
182
  claims: dict[str, Any] = jwt.decode(jwt=token,
182
183
  key=JWT_DECODING_KEY,
183
184
  algorithms=[JWT_DEFAULT_ALGORITHM])
184
- if nature and "nat" in claims and nature != claims.get("nat"):
185
+ if nature and nature != claims.get("nat"):
185
186
  nat: str = "an access" if nature == "A" else "a refresh"
186
187
  err_msg = f"Token is not {nat} token"
187
188
  except Exception as e:
@@ -198,16 +199,18 @@ def jwt_validate_token(errors: list[str] | None,
198
199
  return err_msg is None
199
200
 
200
201
 
201
- def jwt_revoke_tokens(errors: list[str] | None,
202
- account_id: str,
203
- logger: Logger = None) -> bool:
202
+ def jwt_revoke_token(errors: list[str] | None,
203
+ account_id: str,
204
+ refresh_token: str,
205
+ logger: Logger = None) -> bool:
204
206
  """
205
- Revoke all refresh tokens associated with *account_id*.
207
+ Revoke the *refresh_token* associated with *account_id*.
206
208
 
207
209
  Revoke operations require access to a database table defined by *JWT_DB_TABLE*.
208
210
 
209
211
  :param errors: incidental error messages
210
212
  :param account_id: the account identification
213
+ :param refresh_token: the token to be revolked
211
214
  :param logger: optional logger
212
215
  :return: *True* if operation could be performed, *False* otherwise
213
216
  """
@@ -215,16 +218,25 @@ def jwt_revoke_tokens(errors: list[str] | None,
215
218
  result: bool = False
216
219
 
217
220
  if logger:
218
- logger.debug(msg=f"Revoking refresh tokens of '{account_id}'")
221
+ logger.debug(msg=f"Revoking refresh token of '{account_id}'")
219
222
 
220
223
  op_errors: list[str] = []
221
224
  if JWT_DB_ENGINE:
222
- from pypomes_db import db_delete
223
- delete_stmt: str = (f"DELETE FROM {JWT_DB_TABLE} "
224
- f"WHERE {JWT_DB_COL_ACCOUNT} = '{account_id}'")
225
- db_delete(errors=op_errors,
226
- delete_stmt=delete_stmt,
227
- logger=logger)
225
+ from pypomes_db import db_exists, db_delete
226
+ # ruff: noqa: S324
227
+ hasher = hashlib.new(name="md5",
228
+ data=refresh_token.encode())
229
+ token_hash: str = hasher.digest().decode()
230
+ if db_exists(errors=op_errors,
231
+ table=JWT_DB_TABLE,
232
+ where_data={"ds_hash": token_hash},
233
+ logger=logger):
234
+ db_delete(errors=errors,
235
+ delete_stmt=f"DELETE FROM {JWT_DB_TABLE}",
236
+ where_data={"ds_hash": token_hash},
237
+ logger=logger)
238
+ elif not op_errors:
239
+ op_errors.append("Token was not found")
228
240
  else:
229
241
  op_errors.append("Database access for token revocation has not been specified")
230
242
 
@@ -278,7 +290,7 @@ def jwt_get_tokens(errors: list[str] | None,
278
290
  recs: list[tuple[str]] = db_select(errors=op_errors,
279
291
  sel_stmt=f"SELECT {JWT_DB_COL_TOKEN} "
280
292
  f"FROM {JWT_DB_TABLE}",
281
- where_data={JWT_DB_COL_ACCOUNT: f"'{account_id}'"},
293
+ where_data={JWT_DB_COL_ACCOUNT: account_id},
282
294
  logger=logger)
283
295
  if not op_errors and \
284
296
  (len(recs) == 0 or recs[0][0] != refresh_token):
@@ -325,7 +337,8 @@ def jwt_get_claims(errors: list[str] | None,
325
337
  {
326
338
  "header": {
327
339
  "alg": "HS256",
328
- "typ": "JWT"
340
+ "typ": "JWT",
341
+ "kid": "rt466ytRTYH64577uydhDFGHDYJH2341"
329
342
  },
330
343
  "payload": {
331
344
  "birthdate": "1980-01-01",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pypomes_jwt
3
- Version: 0.7.8
3
+ Version: 0.8.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
@@ -0,0 +1,8 @@
1
+ pypomes_jwt/__init__.py,sha256=06WdwiP2m5jtrFjpPSacg4fRd2Dh6gVo93xJhmu73J4,1134
2
+ pypomes_jwt/jwt_constants.py,sha256=6-Jw4ORgf32hRWnaGyVISXMJMtTBk7LdKl3RrDy7Ll0,4328
3
+ pypomes_jwt/jwt_data.py,sha256=gyhGquSQbHevOKIoXmAmjMSwCjXB7pYbI2sY-7sGGO8,19158
4
+ pypomes_jwt/jwt_pomes.py,sha256=PPx-JTlR2dVsLUFlkCiZCxYOnH-BbwqpptBDHO_PIfI,15213
5
+ pypomes_jwt-0.8.0.dist-info/METADATA,sha256=C2fRw5H6are30XZfHLK5IpyqNFlL59Kt9Z0VtJXF07E,599
6
+ pypomes_jwt-0.8.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
7
+ pypomes_jwt-0.8.0.dist-info/licenses/LICENSE,sha256=NdakochSXm_H_-DSL_x2JlRCkYikj3snYYvTwgR5d_c,1086
8
+ pypomes_jwt-0.8.0.dist-info/RECORD,,
@@ -1,8 +0,0 @@
1
- pypomes_jwt/__init__.py,sha256=dkWeYPNwypjwFuTjx4YtC8QV9ihykF4xcJJ7x86Wc5g,1130
2
- pypomes_jwt/jwt_constants.py,sha256=8QYpYbpuNfeq15Gwkww2VtEXHFOIkZ1pPS-GU_hifs4,4491
3
- pypomes_jwt/jwt_data.py,sha256=aZCb6SAgZEz5dFhLWcinr1mc9HZndYQ1gSw5z-5cnCA,19622
4
- pypomes_jwt/jwt_pomes.py,sha256=nrvu7zIAp0Blq3wAHbvtKLKOVBGlqCttTDOu-LsZxrE,14650
5
- pypomes_jwt-0.7.8.dist-info/METADATA,sha256=k_n1-ruFPdSXdEHhrAvLDNFoxeXlMAuHZh7Qg5X7Dy0,599
6
- pypomes_jwt-0.7.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
7
- pypomes_jwt-0.7.8.dist-info/licenses/LICENSE,sha256=NdakochSXm_H_-DSL_x2JlRCkYikj3snYYvTwgR5d_c,1086
8
- pypomes_jwt-0.7.8.dist-info/RECORD,,