pypomes-jwt 0.6.4__tar.gz → 0.6.6__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.6.4
3
+ Version: 0.6.6
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.6.4"
9
+ version = "0.6.6"
10
10
  authors = [
11
11
  { name="GT Nunes", email="wisecoder01@gmail.com" }
12
12
  ]
@@ -5,8 +5,8 @@ from .jwt_pomes import (
5
5
  JWT_ENDPOINT_URL,
6
6
  JWT_ACCESS_MAX_AGE, JWT_REFRESH_MAX_AGE,
7
7
  JWT_HS_SECRET_KEY, JWT_RSA_PRIVATE_KEY, JWT_RSA_PUBLIC_KEY,
8
- jwt_needed, jwt_verify_request, jwt_service,
9
- jwt_get_token_claims, jwt_get_token, jwt_get_token_data,
8
+ jwt_needed, jwt_verify_request, jwt_claims, jwt_token,
9
+ jwt_get_token_data, jwt_get_token_claims,
10
10
  jwt_assert_access, jwt_set_access, jwt_remove_access
11
11
  )
12
12
 
@@ -17,8 +17,8 @@ __all__ = [
17
17
  "JWT_ENDPOINT_URL",
18
18
  "JWT_ACCESS_MAX_AGE", "JWT_REFRESH_MAX_AGE",
19
19
  "JWT_HS_SECRET_KEY", "JWT_RSA_PRIVATE_KEY", "JWT_RSA_PUBLIC_KEY",
20
- "jwt_needed", "jwt_verify_request", "jwt_service",
21
- "jwt_get_token_claims", "jwt_get_token", "jwt_get_token_data",
20
+ "jwt_needed", "jwt_verify_request", "jwt_claims", "jwt_token",
21
+ "jwt_get_token_data", "jwt_get_token_claims",
22
22
  "jwt_assert_access", "jwt_set_access", "jwt_remove_access"
23
23
  ]
24
24
 
@@ -39,14 +39,14 @@ class JwtData:
39
39
  # "aud": <string> # audience
40
40
  # "nbt": <timestamp> # not before time
41
41
  },
42
- "public-claims": {
42
+ "public-claims": { # public claims (may be empty)
43
43
  "birthdate": <string>, # subject's birth date
44
44
  "email": <string>, # subject's email
45
45
  "gender": <string>, # subject's gender
46
46
  "name": <string>, # subject's name
47
47
  "roles": <List[str]> # subject roles
48
48
  },
49
- "custom-claims": { # custom claims
49
+ "custom-claims": { # custom claims (may be empty)
50
50
  "<custom-claim-key-1>": "<custom-claim-value-1>",
51
51
  ...
52
52
  "<custom-claim-key-n>": "<custom-claim-value-n>"
@@ -166,6 +166,7 @@ class JwtData:
166
166
 
167
167
  def get_token_data(self,
168
168
  account_id: str,
169
+ superceding_claims: dict[str, Any] = None,
169
170
  logger: Logger = None) -> dict[str, Any]:
170
171
  """
171
172
  Obtain and return the JWT token for *account_id*, along with its duration.
@@ -178,6 +179,7 @@ class JwtData:
178
179
  }
179
180
 
180
181
  :param account_id: the account identification
182
+ :param superceding_claims: if provided, may supercede registered custom claims
181
183
  :param logger: optional logger
182
184
  :return: the JWT token data, or *None* if error
183
185
  :raises InvalidTokenError: token is invalid
@@ -205,9 +207,12 @@ class JwtData:
205
207
  control_data: dict[str, Any] = item_data.get("control-data")
206
208
  reserved_claims: dict[str, Any] = item_data.get("reserved-claims")
207
209
  custom_claims: dict[str, Any] = item_data.get("custom-claims")
208
- just_now: int = int(datetime.now(tz=timezone.utc).timestamp())
210
+ if superceding_claims:
211
+ custom_claims = custom_claims.copy()
212
+ custom_claims.update(m=superceding_claims)
209
213
 
210
214
  # obtain a new token, if the current token has expired
215
+ just_now: int = int(datetime.now(tz=timezone.utc).timestamp())
211
216
  if just_now > reserved_claims.get("exp"):
212
217
  # where is the JWT service provider ?
213
218
  if control_data.get("remote-provider"):
@@ -137,42 +137,12 @@ def jwt_remove_access(account_id: str,
137
137
  logger=logger)
138
138
 
139
139
 
140
- def jwt_get_token(errors: list[str],
141
- account_id: str,
142
- logger: Logger = None) -> str:
143
- """
144
- Obtain and return a JWT token for *account_id*.
145
-
146
- :param errors: incidental error messages
147
- :param account_id: the account identification
148
- :param logger: optional logger
149
- :return: the JWT token, or *None* if an error ocurred
150
- """
151
- # inicialize the return variable
152
- result: str | None = None
153
-
154
- if logger:
155
- logger.debug(msg=f"Obtain a JWT token for '{account_id}'")
156
-
157
- try:
158
- token_data: dict[str, Any] = __jwt_data.get_token_data(account_id=account_id,
159
- logger=logger)
160
- result = token_data.get("access_token")
161
- if logger:
162
- logger.debug(f"Token is '{result}'")
163
- except Exception as e:
164
- if logger:
165
- logger.error(msg=str(e))
166
- errors.append(str(e))
167
-
168
- return result
169
-
170
-
171
140
  def jwt_get_token_data(errors: list[str],
172
141
  account_id: str,
142
+ superceding_claims: dict[str, Any] = None,
173
143
  logger: Logger = None) -> dict[str, Any]:
174
144
  """
175
- Obtain and return the JWT token associated with *account_id*, along with its duration.
145
+ Obtain and return the JWT token data associated with *account_id*.
176
146
 
177
147
  Structure of the return data:
178
148
  {
@@ -183,6 +153,7 @@ def jwt_get_token_data(errors: list[str],
183
153
 
184
154
  :param errors: incidental error messages
185
155
  :param account_id: the account identification
156
+ :param superceding_claims: if provided, may supercede registered custom claims
186
157
  :param logger: optional logger
187
158
  :return: the JWT token data, or *None* if error
188
159
  """
@@ -193,6 +164,7 @@ def jwt_get_token_data(errors: list[str],
193
164
  logger.debug(msg=f"Retrieve JWT token data for '{account_id}'")
194
165
  try:
195
166
  result = __jwt_data.get_token_data(account_id=account_id,
167
+ superceding_claims=superceding_claims,
196
168
  logger=logger)
197
169
  if logger:
198
170
  logger.debug(msg=f"Data is '{result}'")
@@ -290,20 +262,61 @@ def jwt_verify_request(request: Request,
290
262
  return result
291
263
 
292
264
 
293
- def jwt_service(account_id: str = None,
294
- service_params: dict[str, Any] = None,
295
- logger: Logger = None) -> Response:
265
+ def jwt_claims(token: str = None) -> Response:
296
266
  """
297
- Entry point for obtaining JWT tokens.
267
+ REST service entry point for retrieving the claims of a JWT token.
298
268
 
299
- In order to be serviced, the invoker must send, as parameter *service_params* or in the body of the request:
269
+ Structure of the return data:
270
+ {
271
+ "<claim-1>": <value-of-claim-1>,
272
+ ...
273
+ "<claim-n>": <value-of-claim-n>
274
+ }
275
+
276
+ :param token: the JWT token
277
+ :return: a *Response* containing the requested JWT token claims, or reporting an error
278
+ """
279
+ # declare the return variable
280
+ result: Response
281
+
282
+ # retrieve the token
283
+ # noinspection PyUnusedLocal
284
+ if not token:
285
+ token = request.values.get("token")
286
+ if not token:
287
+ with contextlib.suppress(Exception):
288
+ token = request.get_json().get("token")
289
+
290
+ # has the token been obtained ?
291
+ if token:
292
+ # yes, obtain the token data
293
+ try:
294
+ token_claims: dict[str, Any] = __jwt_data.get_token_claims(token=token)
295
+ result = jsonify(token_claims)
296
+ except Exception as e:
297
+ # claims extraction failed
298
+ result = Response(response=str(e),
299
+ status=400)
300
+ else:
301
+ # no, report the problem
302
+ result = Response(response="Invalid parameters",
303
+ status=400)
304
+
305
+ return result
306
+
307
+
308
+ def jwt_token(service_params: dict[str, Any] = None) -> Response:
309
+ """
310
+ REST service entry point for obtaining JWT tokens.
311
+
312
+ The requester must send, as parameter *service_params* or in the body of the request:
300
313
  {
301
314
  "account-id": "<string>" - required account identification
302
- "<custom-claim-key-1>": "<custom-claim-value-1>", - optional custom claims
315
+ "<custom-claim-key-1>": "<custom-claim-value-1>", - optional superceding custom claims
303
316
  ...
304
317
  "<custom-claim-key-n>": "<custom-claim-value-n>"
305
318
  }
306
- If provided, the additional custom claims will be sent to the remote provider, if applicable
319
+ If provided, the superceding custom claims will be sent to the remote provider, if applicable
307
320
  (custom claims currently registered for the account may be overridden).
308
321
 
309
322
 
@@ -314,55 +327,33 @@ def jwt_service(account_id: str = None,
314
327
  "expires_in": <seconds-to-expiration>
315
328
  }
316
329
 
317
- :param account_id: the account identification, alternatively passed in JSON
318
330
  :param service_params: the optional JSON containing the request parameters (defaults to JSON in body)
319
- :param logger: optional logger
320
- :return: a *Response* containing the requested JWT token and its duration, or reporting an error
331
+ :return: a *Response* containing the requested JWT token data, or reporting an error
321
332
  """
322
333
  # declare the return variable
323
334
  result: Response
324
335
 
325
- if logger:
326
- msg: str = "Service a JWT request"
327
- if request:
328
- msg += f" from '{request.base_url}'"
329
- logger.debug(msg=msg)
330
-
331
336
  # retrieve the parameters
332
337
  # noinspection PyUnusedLocal
333
338
  params: dict[str, Any] = service_params or {}
334
339
  if not params:
335
340
  with contextlib.suppress(Exception):
336
341
  params = request.get_json()
337
- if not account_id:
338
- account_id = params.get("account-id")
342
+ account_id: str | None = params.pop("account-id", None)
339
343
 
340
344
  # has the account been identified ?
341
345
  if account_id:
342
- # yes, proceed
343
- if logger:
344
- logger.debug(msg=f"Account identification is '{account_id}'")
345
- item_data: dict[str, dict[str, Any]] = __jwt_data.get_access_data(account_id=account_id,
346
- logger=logger) or {}
347
- custom_claims: dict[str, Any] = item_data.get("custom-claims").copy()
348
- for key, value in params.items():
349
- custom_claims[key] = value
350
-
351
- # obtain the token data
346
+ # yes, obtain the token data
352
347
  try:
353
348
  token_data: dict[str, Any] = __jwt_data.get_token_data(account_id=account_id,
354
- logger=logger)
349
+ superceding_claims=params)
355
350
  result = jsonify(token_data)
356
351
  except Exception as e:
357
352
  # token validation failed
358
- if logger:
359
- logger.error(msg=str(e))
360
353
  result = Response(response=str(e),
361
354
  status=401)
362
355
  else:
363
356
  # no, report the problem
364
- if logger:
365
- logger.debug(msg=f"Invalid parameters {service_params}")
366
357
  result = Response(response="Invalid parameters",
367
358
  status=401)
368
359
 
File without changes
File without changes
File without changes
File without changes