pypomes-jwt 0.9.1__tar.gz → 0.9.2__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.1
3
+ Version: 0.9.2
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
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
6
6
 
7
7
  [project]
8
8
  name = "pypomes_jwt"
9
- version = "0.9.1"
9
+ version = "0.9.2"
10
10
  authors = [
11
11
  { name="GT Nunes", email="wisecoder01@gmail.com" }
12
12
  ]
@@ -61,7 +61,7 @@ def jwt_verify_request(request: Request,
61
61
  logger.debug(msg="Bearer token was retrieved")
62
62
  errors: list[str] = []
63
63
  jwt_validate_token(errors=errors,
64
- nature=["A"],
64
+ natures=["A"],
65
65
  token=token)
66
66
  if errors:
67
67
  err_msg = "; ".join(errors)
@@ -157,20 +157,20 @@ def jwt_remove_account(account_id: str,
157
157
 
158
158
  def jwt_validate_token(errors: list[str] | None,
159
159
  token: str,
160
- nature: list[str] = None,
160
+ natures: list[str] = None,
161
161
  account_id: str = None,
162
162
  logger: Logger = None) -> dict[str, Any] | None:
163
163
  """
164
164
  Verify if *token* ia a valid JWT token.
165
165
 
166
166
  Raise an appropriate exception if validation failed.
167
- if *nature* is provided, it is checked whether *token* has been locally issued and is of a appropriate nature.
168
- A token issued locally has the header claim *kid* starting with "A" (for *Access*) or "R" (for *Refresh*),
169
- followed by its id in the token database.
167
+ if *nature* is provided, verify whether *token* has been locally issued and is of a appropriate nature.
168
+ A token issued locally has the header claim *kid* starting with *A* (for *Access*) or *R* (for *Refresh*),
169
+ followed by its id in the token database, or as a single letter in the range *[B-Z]*, less *R*.
170
170
 
171
171
  :param errors: incidental error messages
172
172
  :param token: the token to be validated
173
- :param nature: one of more prefixes identifying the nature of locally issued tokens
173
+ :param natures: one or more prefixes identifying the nature of locally issued tokens
174
174
  :param account_id: optionally, validate the token's account owner
175
175
  :param logger: optional logger
176
176
  :return: The token's claims (header and payload) if if is valid, *None* otherwise
@@ -188,7 +188,7 @@ def jwt_validate_token(errors: list[str] | None,
188
188
  op_errors: list[str] = []
189
189
 
190
190
  # retrieve token data from database
191
- if nature and not (token_kid and token_kid[0:1] in nature):
191
+ if natures and not (token_kid and token_kid[0:1] in natures):
192
192
  op_errors.append("Invalid token")
193
193
  elif token_kid and len(token_kid) > 1 and token_kid[0:1].isupper() and token[1:].isdigit():
194
194
  # token was likely issued locally
@@ -276,7 +276,7 @@ def jwt_revoke_token(errors: list[str] | None,
276
276
  op_errors: list[str] = []
277
277
  token_claims: dict[str, Any] = jwt_validate_token(errors=op_errors,
278
278
  token=refresh_token,
279
- nature=["A", "R"],
279
+ natures=["A", "R"],
280
280
  account_id=account_id,
281
281
  logger=logger)
282
282
  if not op_errors:
@@ -303,22 +303,22 @@ def jwt_issue_token(errors: list[str] | None,
303
303
  account_id: str,
304
304
  nature: str,
305
305
  duration: int,
306
- claims: dict[str, Any],
307
306
  grace_interval: int = None,
307
+ claims: dict[str, Any] = None,
308
308
  logger: Logger = None) -> str:
309
309
  """
310
310
  Issue or refresh, and return, a JWT token associated with *account_id*, of the specified *nature*.
311
311
 
312
- The parameter *nature* must be a single uppercase, unaccented letter, different from "A"
313
- (used to indicate *access* tokens) and *R* (used to indicate *refresh* tokens).
312
+ The parameter *nature* must be a single letter in the range *[B-Z]*, less *R*
313
+ (*A* is reserved for *access* tokens, and *R* for *refresh* tokens).
314
314
  The parameter *duration* specifies the token's validity interval (at least 60 seconds).
315
- The token's *claims* should contain the claim *iss*.
315
+ These claims are ignored, if specified in *claims*: *iat*, *iss*, *exp*, *jti*, *nbf*, and *sub*.
316
316
 
317
317
  :param errors: incidental error messages
318
318
  :param account_id: the account identification
319
- :param nature: the token's nature (a single uppercase, unaccented letter different from "A" and "R")
319
+ :param nature: the token's nature, must be a single letter in the range *[B-Z]*, less *R*
320
320
  :param duration: the number of seconds for the token to remain valid (at least 60 seconds)
321
- :param claims: the token's claims (must contain at least the claim "iss")
321
+ :param claims: optional token's claims
322
322
  :param grace_interval: optional interval for the token to become active (in seconds)
323
323
  :param logger: optional logger
324
324
  :return: the JWT token data, or *None* if error
@@ -360,8 +360,10 @@ def jwt_issue_tokens(errors: list[str] | None,
360
360
  """
361
361
  Issue the JWT tokens associated with *account_id*, for access and refresh operations.
362
362
 
363
- If *refresh_token* is provided, its claims are used on issuing the new tokens,
364
- and claims in *account_claims*, if any, are ignored.
363
+ If *refresh_token* is provided, its claims are used on issuing the new tokens, and
364
+ claims in *account_claims*, if any, are ignored. Furthermore, these claims are ignored,
365
+ if provided in *account_claims*: *iat*, *iss*, *exp*, *jti*, *nbf*, and *sub*.
366
+ Other claims specified therein may supercede registered account-related claims.
365
367
 
366
368
  Structure of the return data:
367
369
  {
@@ -389,15 +391,16 @@ def jwt_issue_tokens(errors: list[str] | None,
389
391
  if refresh_token:
390
392
  account_claims = (jwt_validate_token(errors=op_errors,
391
393
  token=refresh_token,
392
- nature=["R"],
394
+ natures=["R"],
393
395
  account_id=account_id,
394
396
  logger=logger) or {}).get("payload")
395
397
  if account_claims:
398
+ account_claims.pop("exp", None)
396
399
  account_claims.pop("iat", None)
397
- account_claims.pop("jti", None)
398
400
  account_claims.pop("iss", None)
399
- account_claims.pop("exp", None)
401
+ account_claims.pop("jti", None)
400
402
  account_claims.pop("nbt", None)
403
+ account_claims.pop("sub", None)
401
404
 
402
405
  if not op_errors:
403
406
  try:
@@ -81,12 +81,12 @@ class JwtRegistry:
81
81
  "nonce": <string> # value used to associate a client session with a token
82
82
 
83
83
  The token header has these items:
84
- "alg": <string> # the algorithm used to sign the token (one of 'HS256', 'HS512', 'RSA256', 'RSA512')
85
- "typ": <string> # the token type (fixed to 'JWT'
84
+ "alg": <string> # the algorithm used to sign the token (one of *HS256*, *HS51*', *RSA256*, *RSA512*)
85
+ "typ": <string> # the token type (fixed to *JWT*
86
86
  "kid": <string> # a reference to the token type and the key to its location in the token database
87
87
 
88
- If issued by the local server, "kid" holds the key to the corresponding record in the token database.
89
- It starts with *A* for (*Access*) or *R* (for *Refresh*) followed its integer key.
88
+ If issued by the local server, "kid" holds the key to the corresponding record in the token database,
89
+ if starting with *A* for (*Access*) or *R* (for *Refresh*), followed an integer.
90
90
  """
91
91
  def __init__(self) -> None:
92
92
  """
@@ -111,7 +111,7 @@ class JwtRegistry:
111
111
  Add to storage the parameters needed to produce and validate JWT tokens for *account_id*.
112
112
 
113
113
  The parameter *claims* may contain account-related claims, only. Ideally, it should contain,
114
- at a minimum, "birthdate", "email", "gender", "name", and "roles".
114
+ at a minimum, *birthdate*, *email*, *gender*, *name*, and *roles*.
115
115
  If the token provider is local, then the token-related claims are created at token issuing time.
116
116
  If the token provider is remote, all claims are sent to it at token request time.
117
117
 
@@ -178,21 +178,21 @@ class JwtRegistry:
178
178
  account_id: str,
179
179
  nature: str,
180
180
  duration: int,
181
- claims: dict[str, Any],
182
181
  grace_interval: int = None,
182
+ claims: dict[str, Any] = None,
183
183
  logger: Logger = None) -> str:
184
184
  """
185
185
  Issue an return a JWT token associated with *account_id*.
186
186
 
187
- The parameter *nature* must be a single uppercase, unaccented letter, different from "A"
188
- (used to indicate *access* tokens) and *R* (used to indicate *refresh* tokens).
187
+ The parameter *nature* must be a single letter in the range *[B-Z]*, less *R*
188
+ (*A* is reserved for *access* tokens, and *R* for *refresh* tokens).
189
189
  The parameter *duration* specifies the token's validity interval (at least 60 seconds).
190
- The token's *claims* should contain the claim *iss*.
190
+ These claims are ignored, if specified in *claims*: *iat*, *iss*, *exp*, *jti*, *nbf*, and *sub*.
191
191
 
192
192
  :param account_id: the account identification
193
- :param nature: the token's nature (a single uppercase, unaccented letter different from "A" and "R")
193
+ :param nature: the token's nature, must be a single letter in the range *[B-Z]*, less *R*
194
194
  :param duration: the number of seconds for the token to remain valid (at least 60 seconds)
195
- :param claims: the token's claims (must contain at least the claim "iss")
195
+ :param claims: optional token's claims
196
196
  :param grace_interval: optional interval for the token to become active (in seconds)
197
197
  :param logger: optional logger
198
198
  :return: the JWT token
@@ -212,13 +212,16 @@ class JwtRegistry:
212
212
  logger.error(err_msg)
213
213
  raise RuntimeError(err_msg)
214
214
 
215
- # make sure account data exists
216
- self.__get_account_data(account_id=account_id,
217
- logger=logger)
215
+ # obtain the account data in storage (may raise an exception)
216
+ account_data: dict[str, Any] = self.__get_account_data(account_id=account_id,
217
+ logger=logger)
218
218
  # issue the token
219
- current_claims: dict[str, Any] = claims.copy()
219
+ current_claims: dict[str, Any] = {}
220
+ if claims:
221
+ current_claims.update(claims)
220
222
  current_claims["jti"] = str_random(size=32,
221
223
  chars=string.ascii_letters + string.digits)
224
+ current_claims["iss"] = account_data.get("reference-url")
222
225
  current_claims["sub"] = account_id
223
226
  just_now: int = int(datetime.now(tz=timezone.utc).timestamp())
224
227
  current_claims["iat"] = just_now
@@ -245,6 +248,9 @@ class JwtRegistry:
245
248
  """
246
249
  Issue and return the JWT access and refresh tokens for *account_id*.
247
250
 
251
+ These claims are ignored, if specified in *account_claims*: *iat*, *iss*, *exp*, *jti*, *nbf*, and *sub*.
252
+ Other claims specified therein may supercede registered account-related claims.
253
+
248
254
  Structure of the return data:
249
255
  {
250
256
  "access_token": <jwt-token>,
@@ -262,7 +268,7 @@ class JwtRegistry:
262
268
  # initialize the return variable
263
269
  result: dict[str, Any] | None = None
264
270
 
265
- # process the data in storage
271
+ # process the account data in storage
266
272
  with (self.access_lock):
267
273
  account_data: dict[str, Any] = self.__get_account_data(account_id=account_id,
268
274
  logger=logger)
File without changes
File without changes
File without changes
File without changes