pypomes-jwt 0.9.0__py3-none-any.whl → 0.9.1__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
@@ -3,13 +3,14 @@ from .jwt_constants import (
3
3
  JWT_DB_PORT, JWT_DB_USER, JWT_DB_PWD,
4
4
  JWT_DB_TABLE, JWT_DB_COL_KID, JWT_DB_COL_ACCOUNT,
5
5
  JWT_DB_COL_ALGORITHM, JWT_DB_COL_DECODER, JWT_DB_COL_TOKEN,
6
- JWT_ACCESS_MAX_AGE, JWT_REFRESH_MAX_AGE,
7
- JWT_ENCODING_KEY, JWT_DECODING_KEY
6
+ JWT_ACCOUNT_LIMIT, JWT_ENCODING_KEY, JWT_DECODING_KEY,
7
+ JWT_ACCESS_MAX_AGE, JWT_REFRESH_MAX_AGE
8
8
  )
9
9
  from .jwt_pomes import (
10
10
  jwt_needed, jwt_verify_request,
11
11
  jwt_assert_account, jwt_set_account, jwt_remove_account,
12
- jwt_get_tokens, jwt_get_claims, jwt_validate_token, jwt_revoke_token
12
+ jwt_issue_token, jwt_issue_tokens, jwt_get_claims,
13
+ jwt_validate_token, jwt_revoke_token
13
14
  )
14
15
 
15
16
  __all__ = [
@@ -18,12 +19,13 @@ __all__ = [
18
19
  "JWT_DB_PORT", "JWT_DB_USER", "JWT_DB_PWD",
19
20
  "JWT_DB_TABLE", "JWT_DB_COL_KID", "JWT_DB_COL_ACCOUNT",
20
21
  "JWT_DB_COL_ALGORITHM", "JWT_DB_COL_DECODER", "JWT_DB_COL_TOKEN",
22
+ "JWT_ACCOUNT_LIMIT", "JWT_ENCODING_KEY", "JWT_DECODING_KEY",
21
23
  "JWT_ACCESS_MAX_AGE", "JWT_REFRESH_MAX_AGE",
22
- "JWT_ENCODING_KEY", "JWT_DECODING_KEY",
23
24
  # jwt_pomes
24
25
  "jwt_needed", "jwt_verify_request",
25
26
  "jwt_assert_account", "jwt_set_account", "jwt_remove_account",
26
- "jwt_get_tokens", "jwt_get_claims", "jwt_validate_token", "jwt_revoke_token"
27
+ "jwt_issue_token", "jwt_issue_tokens", "jwt_get_claims",
28
+ "jwt_validate_token", "jwt_revoke_token"
27
29
  ]
28
30
 
29
31
  from importlib.metadata import version
pypomes_jwt/jwt_pomes.py CHANGED
@@ -11,10 +11,10 @@ from .jwt_constants import (
11
11
  JWT_DEFAULT_ALGORITHM, JWT_DECODING_KEY,
12
12
  JWT_DB_TABLE, JWT_DB_COL_KID, JWT_DB_COL_ALGORITHM, JWT_DB_COL_DECODER
13
13
  )
14
- from .jwt_data import JwtData
14
+ from .jwt_registry import JwtRegistry
15
15
 
16
16
  # the JWT data object
17
- __jwt_data: JwtData = JwtData()
17
+ __jwt_registry: JwtRegistry = JwtRegistry()
18
18
 
19
19
 
20
20
  def jwt_needed(func: callable) -> callable:
@@ -58,7 +58,7 @@ def jwt_verify_request(request: Request,
58
58
  # yes, extract and validate the JWT access token
59
59
  token: str = auth_header.split(" ")[1]
60
60
  if logger:
61
- logger.debug(msg="Token was found")
61
+ logger.debug(msg="Bearer token was retrieved")
62
62
  errors: list[str] = []
63
63
  jwt_validate_token(errors=errors,
64
64
  nature=["A"],
@@ -85,7 +85,7 @@ def jwt_assert_account(account_id: str) -> bool:
85
85
  :param account_id: the account identification
86
86
  :return: *True* if access data exists for *account_id*, *False* otherwise
87
87
  """
88
- return __jwt_data.access_data.get(account_id) is not None
88
+ return __jwt_registry.access_data.get(account_id) is not None
89
89
 
90
90
 
91
91
  def jwt_set_account(account_id: str,
@@ -126,17 +126,17 @@ def jwt_set_account(account_id: str,
126
126
  reference_url = reference_url[:pos]
127
127
 
128
128
  # register the JWT service
129
- __jwt_data.add_account(account_id=account_id,
130
- reference_url=reference_url,
131
- claims=claims,
132
- access_max_age=access_max_age,
133
- refresh_max_age=refresh_max_age,
134
- grace_interval=grace_interval,
135
- token_audience=token_audience,
136
- token_nonce=token_nonce,
137
- request_timeout=request_timeout,
138
- remote_provider=remote_provider,
139
- logger=logger)
129
+ __jwt_registry.add_account(account_id=account_id,
130
+ reference_url=reference_url,
131
+ claims=claims,
132
+ access_max_age=access_max_age,
133
+ refresh_max_age=refresh_max_age,
134
+ grace_interval=grace_interval,
135
+ token_audience=token_audience,
136
+ token_nonce=token_nonce,
137
+ request_timeout=request_timeout,
138
+ remote_provider=remote_provider,
139
+ logger=logger)
140
140
 
141
141
 
142
142
  def jwt_remove_account(account_id: str,
@@ -151,8 +151,8 @@ def jwt_remove_account(account_id: str,
151
151
  if logger:
152
152
  logger.debug(msg=f"Remove access data for '{account_id}'")
153
153
 
154
- return __jwt_data.remove_account(account_id=account_id,
155
- logger=logger)
154
+ return __jwt_registry.remove_account(account_id=account_id,
155
+ logger=logger)
156
156
 
157
157
 
158
158
  def jwt_validate_token(errors: list[str] | None,
@@ -190,7 +190,8 @@ def jwt_validate_token(errors: list[str] | None,
190
190
  # retrieve token data from database
191
191
  if nature and not (token_kid and token_kid[0:1] in nature):
192
192
  op_errors.append("Invalid token")
193
- elif token_kid:
193
+ elif token_kid and len(token_kid) > 1 and token_kid[0:1].isupper() and token[1:].isdigit():
194
+ # token was likely issued locally
194
195
  where_data: dict[str, Any] = {JWT_DB_COL_KID: int(token_kid[1:])}
195
196
  if account_id:
196
197
  where_data[JWT_DB_COL_ACCOUNT] = account_id
@@ -298,13 +299,66 @@ def jwt_revoke_token(errors: list[str] | None,
298
299
  return result
299
300
 
300
301
 
301
- def jwt_get_tokens(errors: list[str] | None,
302
- account_id: str,
303
- account_claims: dict[str, Any] = None,
304
- refresh_token: str = None,
305
- logger: Logger = None) -> dict[str, Any]:
302
+ def jwt_issue_token(errors: list[str] | None,
303
+ account_id: str,
304
+ nature: str,
305
+ duration: int,
306
+ claims: dict[str, Any],
307
+ grace_interval: int = None,
308
+ logger: Logger = None) -> str:
309
+ """
310
+ Issue or refresh, and return, a JWT token associated with *account_id*, of the specified *nature*.
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).
314
+ The parameter *duration* specifies the token's validity interval (at least 60 seconds).
315
+ The token's *claims* should contain the claim *iss*.
316
+
317
+ :param errors: incidental error messages
318
+ :param account_id: the account identification
319
+ :param nature: the token's nature (a single uppercase, unaccented letter different from "A" and "R")
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")
322
+ :param grace_interval: optional interval for the token to become active (in seconds)
323
+ :param logger: optional logger
324
+ :return: the JWT token data, or *None* if error
306
325
  """
307
- Issue or refresh, and return, the JWT token data associated with *account_id*.
326
+ # inicialize the return variable
327
+ result: str | None = None
328
+
329
+ if logger:
330
+ logger.debug(msg=f"Issue a JWT token for '{account_id}'")
331
+ op_errors: list[str] = []
332
+
333
+ try:
334
+ result = __jwt_registry.issue_token(account_id=account_id,
335
+ nature=nature,
336
+ duration=duration,
337
+ claims=claims,
338
+ grace_interval=grace_interval,
339
+ logger=logger)
340
+ if logger:
341
+ logger.debug(msg=f"Token is '{result}'")
342
+ except Exception as e:
343
+ # token issuing failed
344
+ op_errors.append(str(e))
345
+
346
+ if op_errors:
347
+ if logger:
348
+ logger.error("; ".join(op_errors))
349
+ if isinstance(errors, list):
350
+ errors.extend(op_errors)
351
+
352
+ return result
353
+
354
+
355
+ def jwt_issue_tokens(errors: list[str] | None,
356
+ account_id: str,
357
+ account_claims: dict[str, Any] = None,
358
+ refresh_token: str = None,
359
+ logger: Logger = None) -> dict[str, Any]:
360
+ """
361
+ Issue the JWT tokens associated with *account_id*, for access and refresh operations.
308
362
 
309
363
  If *refresh_token* is provided, its claims are used on issuing the new tokens,
310
364
  and claims in *account_claims*, if any, are ignored.
@@ -328,10 +382,11 @@ def jwt_get_tokens(errors: list[str] | None,
328
382
  result: dict[str, Any] | None = None
329
383
 
330
384
  if logger:
331
- logger.debug(msg=f"Retrieve JWT token data for '{account_id}'")
385
+ logger.debug(msg=f"Return JWT token data for '{account_id}'")
332
386
  op_errors: list[str] = []
387
+
388
+ # verify whether this refresh token is legitimate
333
389
  if refresh_token:
334
- # verify whether this refresh token is legitimate
335
390
  account_claims = (jwt_validate_token(errors=op_errors,
336
391
  token=refresh_token,
337
392
  nature=["R"],
@@ -343,11 +398,12 @@ def jwt_get_tokens(errors: list[str] | None,
343
398
  account_claims.pop("iss", None)
344
399
  account_claims.pop("exp", None)
345
400
  account_claims.pop("nbt", None)
401
+
346
402
  if not op_errors:
347
403
  try:
348
- result = __jwt_data.issue_tokens(account_id=account_id,
349
- account_claims=account_claims,
350
- logger=logger)
404
+ result = __jwt_registry.issue_tokens(account_id=account_id,
405
+ account_claims=account_claims,
406
+ logger=logger)
351
407
  if logger:
352
408
  logger.debug(msg=f"Token data is '{result}'")
353
409
  except Exception as e:
@@ -18,9 +18,9 @@ from .jwt_constants import (
18
18
  )
19
19
 
20
20
 
21
- class JwtData:
21
+ class JwtRegistry:
22
22
  """
23
- Shared JWT data for security token access.
23
+ Shared JWT registry for security token access.
24
24
 
25
25
  Instance variables:
26
26
  - access_lock: lock for safe multi-threading access
@@ -59,12 +59,13 @@ class JwtData:
59
59
  as token-related and account-related. All times are UTC.
60
60
 
61
61
  Token-related claims are mostly required claims, and convey information about the token itself:
62
+ # required
62
63
  "exp": <timestamp> # expiration time
63
64
  "iat": <timestamp> # issued at
64
65
  "iss": <string> # issuer (for remote providers, URL to obtain and validate the access tokens)
65
66
  "jti": <string> # JWT id
66
67
  "sub": <string> # subject (the account identification)
67
- "nat": <string> # nature of token (A: access; R: refresh) - locally issued tokens, only
68
+ # optional
68
69
  "aud": <string> # token audience
69
70
  "nbt": <timestamp> # not before time
70
71
 
@@ -72,12 +73,12 @@ class JwtData:
72
73
  Alhough they can be freely specified, these are some of the most commonly used claims:
73
74
  "valid-from": <string> # token's start (<YYYY-MM-DDThh:mm:ss+00:00>)
74
75
  "valid-until": <string> # token's finish (<YYYY-MM-DDThh:mm:ss+00.00>)
75
- "birthdate": <string> # subject's birth date
76
- "email": <string> # subject's email
77
- "gender": <string> # subject's gender
78
- "name": <string> # subject's name
79
- "roles": <List[str]> # subject roles
80
- "nonce": <string> # value used to associate a client session with a token
76
+ "birthdate": <string> # subject's birth date
77
+ "email": <string> # subject's email
78
+ "gender": <string> # subject's gender
79
+ "name": <string> # subject's name
80
+ "roles": <List[str]> # subject roles
81
+ "nonce": <string> # value used to associate a client session with a token
81
82
 
82
83
  The token header has these items:
83
84
  "alg": <string> # the algorithm used to sign the token (one of 'HS256', 'HS512', 'RSA256', 'RSA512')
@@ -173,6 +174,70 @@ class JwtData:
173
174
 
174
175
  return account_data is not None
175
176
 
177
+ def issue_token(self,
178
+ account_id: str,
179
+ nature: str,
180
+ duration: int,
181
+ claims: dict[str, Any],
182
+ grace_interval: int = None,
183
+ logger: Logger = None) -> str:
184
+ """
185
+ Issue an return a JWT token associated with *account_id*.
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).
189
+ The parameter *duration* specifies the token's validity interval (at least 60 seconds).
190
+ The token's *claims* should contain the claim *iss*.
191
+
192
+ :param account_id: the account identification
193
+ :param nature: the token's nature (a single uppercase, unaccented letter different from "A" and "R")
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")
196
+ :param grace_interval: optional interval for the token to become active (in seconds)
197
+ :param logger: optional logger
198
+ :return: the JWT token
199
+ :raises RuntimeError: invalid parameter
200
+ """
201
+ # validate some parameters
202
+ err_msg: str | None = None
203
+ if not isinstance(nature, str) or \
204
+ len(nature) != 1 or nature < "A" or nature > "Z":
205
+ err_msg: str = f"Invalid nature '{nature}'"
206
+ elif not isinstance(claims, dict) or "iss" not in claims:
207
+ err_msg = f"invalid claims '{claims}'"
208
+ elif not isinstance(duration, int) or duration < 60:
209
+ err_msg = f"Invalid duration '{duration}'"
210
+ if err_msg:
211
+ if logger:
212
+ logger.error(err_msg)
213
+ raise RuntimeError(err_msg)
214
+
215
+ # make sure account data exists
216
+ self.__get_account_data(account_id=account_id,
217
+ logger=logger)
218
+ # issue the token
219
+ current_claims: dict[str, Any] = claims.copy()
220
+ current_claims["jti"] = str_random(size=32,
221
+ chars=string.ascii_letters + string.digits)
222
+ current_claims["sub"] = account_id
223
+ just_now: int = int(datetime.now(tz=timezone.utc).timestamp())
224
+ current_claims["iat"] = just_now
225
+ if grace_interval:
226
+ current_claims["nbf"] = just_now + grace_interval
227
+ current_claims["valid-from"] = datetime.fromtimestamp(timestamp=current_claims["nbf"],
228
+ tz=timezone.utc).isoformat()
229
+ else:
230
+ current_claims["valid-from"] = datetime.fromtimestamp(timestamp=current_claims["iat"],
231
+ tz=timezone.utc).isoformat()
232
+ current_claims["exp"] = just_now + duration
233
+ current_claims["valid-until"] = datetime.fromtimestamp(timestamp=current_claims["exp"],
234
+ tz=timezone.utc).isoformat()
235
+ # may raise an exception
236
+ return jwt.encode(payload=current_claims,
237
+ key=JWT_ENCODING_KEY,
238
+ algorithm=JWT_DEFAULT_ALGORITHM,
239
+ headers={"kid": nature})
240
+
176
241
  def issue_tokens(self,
177
242
  account_id: str,
178
243
  account_claims: dict[str, Any] = None,
@@ -191,125 +256,118 @@ class JwtData:
191
256
  :param account_id: the account identification
192
257
  :param account_claims: if provided, may supercede registered account-related claims
193
258
  :param logger: optional logger
194
- :return: the JWT token data, or *None* if error
195
- :raises InvalidTokenError: token is invalid
196
- :raises InvalidKeyError: authentication key is not in the proper format
197
- :raises ExpiredSignatureError: token and refresh period have expired
198
- :raises InvalidSignatureError: signature does not match the one provided as part of the token
199
- :raises ImmatureSignatureError: 'nbf' or 'iat' claim represents a timestamp in the future
200
- :raises InvalidAudienceError: 'aud' claim does not match one of the expected audience
201
- :raises InvalidAlgorithmError: the specified algorithm is not recognized
202
- :raises InvalidIssuerError: 'iss' claim does not match the expected issuer
203
- :raises InvalidIssuedAtError: 'iat' claim is non-numeric
204
- :raises MissingRequiredClaimError: a required claim is not contained in the claimset
205
- :raises RuntimeError: error accessing the token database
259
+ :return: the JWT token data
260
+ :raises RuntimeError: invalid account id, or error accessing the token database
206
261
  """
207
262
  # initialize the return variable
208
263
  result: dict[str, Any] | None = None
209
264
 
210
265
  # process the data in storage
211
266
  with (self.access_lock):
212
- account_data: dict[str, Any] = self.access_data.get(account_id)
213
-
214
- # was the JWT data obtained ?
215
- if account_data:
216
- # yes, proceed
217
- errors: list[str] = []
218
- current_claims: dict[str, Any] = account_data.get("claims").copy()
219
- if account_claims:
220
- current_claims.update(account_claims)
221
- current_claims["jti"] = str_random(size=32,
222
- chars=string.ascii_letters + string.digits)
223
- current_claims["sub"] = account_id
224
- current_claims["iss"] = account_data.get("reference-url")
225
-
226
- # where is the JWT service provider ?
227
- if account_data.get("remote-provider"):
228
- # JWT service is being provided by a remote server
229
- # Structure of the return data:
230
- # {
231
- # "access_token": <jwt-token>,
232
- # "created_in": <timestamp>,
233
- # "expires_in": <seconds-to-expiration>,
234
- # "refresh_token": <jwt-token>
235
- # ...
236
- # }
237
- result = _jwt_request_token(errors=errors,
238
- reference_url=current_claims.get("iss"),
239
- claims=current_claims,
240
- timeout=account_data.get("request-timeout"),
241
- logger=logger)
242
- if errors:
243
- raise RuntimeError("; ".join(errors))
267
+ account_data: dict[str, Any] = self.__get_account_data(account_id=account_id,
268
+ logger=logger)
269
+ current_claims: dict[str, Any] = account_data.get("claims").copy()
270
+ if account_claims:
271
+ current_claims.update(account_claims)
272
+ current_claims["jti"] = str_random(size=32,
273
+ chars=string.ascii_letters + string.digits)
274
+ current_claims["sub"] = account_id
275
+ current_claims["iss"] = account_data.get("reference-url")
276
+ errors: list[str] = []
277
+
278
+ # where is the JWT service provider ?
279
+ if account_data.get("remote-provider"):
280
+ # JWT service is being provided by a remote server
281
+ result = _jwt_request_token(errors=errors,
282
+ reference_url=current_claims.get("iss"),
283
+ claims=current_claims,
284
+ timeout=account_data.get("request-timeout"),
285
+ logger=logger)
286
+ if errors:
287
+ raise RuntimeError("; ".join(errors))
288
+ else:
289
+ # JWT service is being provided locally
290
+ just_now: int = int(datetime.now(tz=timezone.utc).timestamp())
291
+ current_claims["iat"] = just_now
292
+ grace_interval = account_data.get("grace-interval")
293
+ if grace_interval:
294
+ current_claims["nbf"] = just_now + grace_interval
295
+ current_claims["valid-from"] = datetime.fromtimestamp(timestamp=current_claims["nbf"],
296
+ tz=timezone.utc).isoformat()
244
297
  else:
245
- # JWT service is being provided locally
246
- just_now: int = int(datetime.now(tz=timezone.utc).timestamp())
247
- current_claims["iat"] = just_now
248
- grace_interval = account_data.get("grace-interval")
249
- if grace_interval:
250
- account_data["nbf"] = just_now + grace_interval
251
- current_claims["valid-from"] = datetime.fromtimestamp(timestamp=current_claims["nbf"],
252
- tz=timezone.utc).isoformat()
253
- else:
254
- current_claims["valid-from"] = datetime.fromtimestamp(timestamp=current_claims["iat"],
255
- tz=timezone.utc).isoformat()
256
- # issue a candidate refresh token first, and persist it
257
- current_claims["exp"] = just_now + account_data.get("refresh-max-age")
258
- current_claims["valid-until"] = datetime.fromtimestamp(timestamp=current_claims["exp"],
259
- tz=timezone.utc).isoformat()
260
- # may raise an exception
261
- refresh_token: str = jwt.encode(payload=current_claims,
262
- key=JWT_ENCODING_KEY,
263
- algorithm=JWT_DEFAULT_ALGORITHM,
264
- headers={"kid": "R0"})
265
- # obtain a DB connection (may raise an exception)
266
- db_conn: Any = db_connect(errors=errors,
267
- logger=logger)
268
- # persist the candidate token (may raise an exception)
269
- token_id: int = _jwt_persist_token(errors=errors,
270
- account_id=account_id,
271
- jwt_token=refresh_token,
272
- db_conn=db_conn,
273
- logger=logger)
274
- # issue the definitive refresh token
275
- refresh_token = jwt.encode(payload=current_claims,
298
+ current_claims["valid-from"] = datetime.fromtimestamp(timestamp=current_claims["iat"],
299
+ tz=timezone.utc).isoformat()
300
+ # issue a candidate refresh token first, and persist it
301
+ current_claims["exp"] = just_now + account_data.get("refresh-max-age")
302
+ current_claims["valid-until"] = datetime.fromtimestamp(timestamp=current_claims["exp"],
303
+ tz=timezone.utc).isoformat()
304
+ # may raise an exception
305
+ refresh_token: str = jwt.encode(payload=current_claims,
306
+ key=JWT_ENCODING_KEY,
307
+ algorithm=JWT_DEFAULT_ALGORITHM,
308
+ headers={"kid": "R0"})
309
+ # obtain a DB connection (may raise an exception)
310
+ db_conn: Any = db_connect(errors=errors,
311
+ logger=logger)
312
+ # persist the candidate token (may raise an exception)
313
+ token_id: int = _jwt_persist_token(errors=errors,
314
+ account_id=account_id,
315
+ jwt_token=refresh_token,
316
+ db_conn=db_conn,
317
+ logger=logger)
318
+ # issue the definitive refresh token
319
+ refresh_token = jwt.encode(payload=current_claims,
320
+ key=JWT_ENCODING_KEY,
321
+ algorithm=JWT_DEFAULT_ALGORITHM,
322
+ headers={"kid": f"R{token_id}"})
323
+ # persist it
324
+ db_update(errors=errors,
325
+ update_stmt=f"UPDATE {JWT_DB_TABLE}",
326
+ update_data={JWT_DB_COL_TOKEN: refresh_token},
327
+ where_data={JWT_DB_COL_KID: token_id},
328
+ connection=db_conn,
329
+ logger=logger)
330
+ # commit the transaction
331
+ db_commit(errors=errors,
332
+ connection=db_conn,
333
+ logger=logger)
334
+ if errors:
335
+ raise RuntimeError("; ".join(errors))
336
+
337
+ # issue the access token
338
+ current_claims["exp"] = just_now + account_data.get("access-max-age")
339
+ # may raise an exception
340
+ access_token: str = jwt.encode(payload=current_claims,
276
341
  key=JWT_ENCODING_KEY,
277
342
  algorithm=JWT_DEFAULT_ALGORITHM,
278
- headers={"kid": f"R{token_id}"})
279
- # persist it
280
- db_update(errors=errors,
281
- update_stmt=f"UPDATE {JWT_DB_TABLE}",
282
- update_data={JWT_DB_COL_TOKEN: refresh_token},
283
- where_data={JWT_DB_COL_KID: token_id},
284
- connection=db_conn,
285
- logger=logger)
286
- # commit the transaction
287
- db_commit(errors=errors,
288
- connection=db_conn,
289
- logger=logger)
290
- if errors:
291
- raise RuntimeError("; ".join(errors))
292
-
293
- # issue the access token
294
- current_claims["exp"] = just_now + account_data.get("access-max-age")
295
- # may raise an exception
296
- access_token: str = jwt.encode(payload=current_claims,
297
- key=JWT_ENCODING_KEY,
298
- algorithm=JWT_DEFAULT_ALGORITHM,
299
- headers={"kid": f"A{token_id}"})
300
- # return the token data
301
- result = {
302
- "access_token": access_token,
303
- "created_in": current_claims.get("iat"),
304
- "expires_in": current_claims.get("exp"),
305
- "refresh_token": refresh_token
306
- }
307
- else:
308
- # JWT access data not found
309
- err_msg: str = f"No JWT access data found for '{account_id}'"
310
- if logger:
311
- logger.error(err_msg)
312
- raise RuntimeError(err_msg)
343
+ headers={"kid": f"A{token_id}"})
344
+ # return the token data
345
+ result = {
346
+ "access_token": access_token,
347
+ "created_in": current_claims.get("iat"),
348
+ "expires_in": current_claims.get("exp"),
349
+ "refresh_token": refresh_token
350
+ }
351
+
352
+ return result
353
+
354
+ def __get_account_data(self,
355
+ account_id: str,
356
+ logger: Logger = None) -> dict[str, Any]:
357
+ """
358
+ Retrieve the JWT access data associated with *account_id*.
359
+
360
+ :return: the JWT access data associated with *account_id*
361
+ :raises RuntimeError: No JWT access data exists for *account_id*
362
+ """
363
+ # retrieve the access data
364
+ result: dict[str, Any] = self.access_data.get(account_id)
365
+ if not result:
366
+ # JWT access data not found
367
+ err_msg: str = f"No JWT access data found for '{account_id}'"
368
+ if logger:
369
+ logger.error(err_msg)
370
+ raise RuntimeError(err_msg)
313
371
 
314
372
  return result
315
373
 
@@ -389,7 +447,7 @@ def _jwt_persist_token(errors: list[str],
389
447
  :param db_conn: the database connection to use
390
448
  :param logger: optional logger
391
449
  :return: the storage id of the inserted token
392
- :raises RuntimeError: error accessing the revocation database
450
+ :raises RuntimeError: error accessing the token database
393
451
  """
394
452
  from pypomes_db import db_select, db_insert, db_delete
395
453
  from .jwt_pomes import jwt_get_claims
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pypomes_jwt
3
- Version: 0.9.0
3
+ Version: 0.9.1
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=6AISZOs7JP695PnMSsYyKfoiMXvSzXffkv3nKw7qA1A,1356
2
+ pypomes_jwt/jwt_constants.py,sha256=IQV39AiZKGuU8XxZBgJ-KJZQZ_mmnxyOnRZeuxlqDRk,4045
3
+ pypomes_jwt/jwt_pomes.py,sha256=qm8COn0vPts-I0n5pnA_5phm-5wkV82dicAf3bvQ8R8,19734
4
+ pypomes_jwt/jwt_registry.py,sha256=MWJy1bxXdgVySmBYJCdK_721U6tJXk3BHrYVChLjCR8,25613
5
+ pypomes_jwt-0.9.1.dist-info/METADATA,sha256=b5EpwtK-c0JCi3QjRt6W_QoQXg8nYkUjJHbD0pTGgF4,632
6
+ pypomes_jwt-0.9.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
7
+ pypomes_jwt-0.9.1.dist-info/licenses/LICENSE,sha256=NdakochSXm_H_-DSL_x2JlRCkYikj3snYYvTwgR5d_c,1086
8
+ pypomes_jwt-0.9.1.dist-info/RECORD,,
@@ -1,8 +0,0 @@
1
- pypomes_jwt/__init__.py,sha256=P7rT6ZVE2BzU3ntYOr83H5iOf5JcCmjDUYakNbrRAP0,1266
2
- pypomes_jwt/jwt_constants.py,sha256=IQV39AiZKGuU8XxZBgJ-KJZQZ_mmnxyOnRZeuxlqDRk,4045
3
- pypomes_jwt/jwt_data.py,sha256=keC95-2DgWYZIzAtYEbtLtfkFSH-V_sTBiTbszsZqho,23286
4
- pypomes_jwt/jwt_pomes.py,sha256=0C4u9UnzqM1ZWTW4rpw18kyvnR1biLEq5nAwZ8MMrz8,17244
5
- pypomes_jwt-0.9.0.dist-info/METADATA,sha256=0KthAWPxm04v3KMnYfsaHal4_kN0tFj8er2a52A__AI,632
6
- pypomes_jwt-0.9.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
7
- pypomes_jwt-0.9.0.dist-info/licenses/LICENSE,sha256=NdakochSXm_H_-DSL_x2JlRCkYikj3snYYvTwgR5d_c,1086
8
- pypomes_jwt-0.9.0.dist-info/RECORD,,