pypomes-jwt 0.9.5__tar.gz → 0.9.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.9.5
3
+ Version: 0.9.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.9.5"
9
+ version = "0.9.6"
10
10
  authors = [
11
11
  { name="GT Nunes", email="wisecoder01@gmail.com" }
12
12
  ]
@@ -89,47 +89,48 @@ def jwt_assert_account(account_id: str) -> bool:
89
89
 
90
90
 
91
91
  def jwt_set_account(account_id: str,
92
- reference_url: str,
93
92
  claims: dict[str, Any],
94
93
  access_max_age: int = JWT_ACCESS_MAX_AGE,
95
94
  refresh_max_age: int = JWT_REFRESH_MAX_AGE,
96
95
  grace_interval: int = None,
97
96
  request_timeout: int = None,
98
- remote_provider: bool = None,
97
+ remote_url: str = None,
99
98
  logger: Logger = None) -> None:
100
99
  """
101
- Set the data needed to obtain JWT tokens for *account_id*.
100
+ Establish the data needed to obtain JWT tokens for *account_id*.
102
101
 
103
102
  :param account_id: the account identification
104
- :param reference_url: the reference URL (for remote providers, URL to obtain and validate the JWT tokens)
105
103
  :param claims: the JWT claimset, as key-value pairs
106
104
  :param access_max_age: access token duration, in seconds
107
105
  :param refresh_max_age: refresh token duration, in seconds
108
106
  :param grace_interval: optional time to wait for token to be valid, in seconds
109
107
  :param request_timeout: timeout for the requests to the reference URL
110
- :param remote_provider: whether the JWT provider is a remote server
108
+ :param remote_url: remote server's URL to obtain the JWT tokens from
111
109
  :param logger: optional logger
112
110
  """
113
111
  if logger:
114
112
  logger.debug(msg=f"Register account data for '{account_id}'")
115
113
 
116
- # extract the claims provided in the reference URL's query string
117
- pos: int = reference_url.find("?")
118
- if pos > 0:
119
- params: list[str] = reference_url[pos+1:].split(sep="&")
120
- for param in params:
121
- claims[param.split("=")[0]] = param.split("=")[1]
122
- reference_url = reference_url[:pos]
114
+ # extract the claims provided in the remote_url URL's query string
115
+ if remote_url:
116
+ pos: int = remote_url.find("?")
117
+ if pos > 0:
118
+ claims = claims or {}
119
+ params: list[str] = remote_url[pos+1:].split(sep="&")
120
+ for param in params:
121
+ key: str = param.split("=")[0]
122
+ value: str = param.split("=")[1]
123
+ claims[key] = value
124
+ remote_url = remote_url[:pos]
123
125
 
124
126
  # register the JWT service
125
127
  __jwt_registry.add_account(account_id=account_id,
126
- reference_url=reference_url,
127
128
  claims=claims,
128
129
  access_max_age=access_max_age,
129
130
  refresh_max_age=refresh_max_age,
130
131
  grace_interval=grace_interval,
131
132
  request_timeout=request_timeout,
132
- remote_provider=remote_provider,
133
+ remote_url=remote_url,
133
134
  logger=logger)
134
135
 
135
136
 
@@ -27,23 +27,22 @@ class JwtRegistry:
27
27
  - access_data: dictionary holding the JWT token data, organized by account id:
28
28
  {
29
29
  <account-id>: {
30
- "reference-url": # the reference URL
31
- "remote-provider": <bool>, # whether the JWT provider is a remote server
32
- "request-timeout": <int>, # in seconds - defaults to no timeout
33
- "access-max-age": <int>, # in seconds - defaults to JWT_ACCESS_MAX_AGE
34
- "refresh-max-age": <int>, # in seconds - defaults to JWT_REFRESH_MAX_AGE
35
- "grace-interval": <int> # time to wait for token to be valid, in seconds
36
- "request-timeout": <int> # timeout for the requests to the reference URL (in seconds)
30
+ "access-max-age": <int>, # defaults to JWT_ACCESS_MAX_AGE (in seconds)
31
+ "refresh-max-age": <int>, # defaults to JWT_REFRESH_MAX_AGE (in seconds)
32
+ "grace-interval": <int>, # time to wait for token to be valid, in seconds
33
+ "request-timeout": <int>, # timeout for the requests to the reference URL (in seconds)
34
+ "remote_url": <string>, # remote server's URL to obtain the JWT tokens from
37
35
  "claims": {
38
- "valid-from": <string> # token's start (<YYYY-MM-DDThh:mm:ss+00:00>)
39
- "valid-until": <string> # token's finish (<YYYY-MM-DDThh:mm:ss+00:00>)
40
- # optional
36
+ "iss": <string>, # token'ss issuer
41
37
  "birthdate": <string>, # subject's birth date
42
38
  "email": <string>, # subject's email
43
39
  "gender": <string>, # subject's gender
44
40
  "name": <string>, # subject's name
45
41
  "roles": <List[str]>, # subject roles
46
42
  "nonce": <string>, # used to associate a Client session with a token
43
+ # output, only
44
+ "valid-from": <string>, # token's start (<YYYY-MM-DDThh:mm:ss+00:00>)
45
+ "valid-until": <string>, # token's finish (<YYYY-MM-DDThh:mm:ss+00:00>)
47
46
  ...
48
47
  }
49
48
  },
@@ -58,30 +57,30 @@ class JwtRegistry:
58
57
 
59
58
  Token-related claims are mostly required claims, and convey information about the token itself:
60
59
  # required
61
- "exp": <timestamp> # expiration time
62
- "iat": <timestamp> # issued at
63
- "iss": <string> # issuer (for remote providers, URL to obtain and validate the access tokens)
64
- "jti": <string> # JWT id
65
- "sub": <string> # subject (the account identification)
60
+ "exp": <timestamp> expiration time
61
+ "iat": <timestamp> issued at
62
+ "iss": <string> token's issuer
63
+ "jti": <string> JWT id
64
+ "sub": <string> subject (the account identification)
66
65
  # optional
67
- "aud": <string> # token audience
68
- "nbt": <timestamp> # not before time
66
+ "aud": <string> token audience
67
+ "nbt": <timestamp> not before time
69
68
 
70
69
  Account-related claims are optional claims, and convey information about the registered account they belong to.
71
70
  Alhough they can be freely specified, these are some of the most commonly used claims:
72
- "valid-from": <string> # token's start (<YYYY-MM-DDThh:mm:ss+00:00>)
73
- "valid-until": <string> # token's finish (<YYYY-MM-DDThh:mm:ss+00.00>)
74
- "birthdate": <string> # subject's birth date
75
- "email": <string> # subject's email
76
- "gender": <string> # subject's gender
77
- "name": <string> # subject's name
78
- "roles": <List[str]> # subject roles
79
- "nonce": <string> # used to associate a client session with a token
71
+ "valid-from": <string> token's start (<YYYY-MM-DDThh:mm:ss+00:00>)
72
+ "valid-until": <string> token's finish (<YYYY-MM-DDThh:mm:ss+00.00>)
73
+ "birthdate": <string> subject's birth date
74
+ "email": <string> subject's email
75
+ "gender": <string> subject's gender
76
+ "name": <string> subject's name
77
+ "roles": <List[str]> subject roles
78
+ "nonce": <string> used to associate a client session with a token
80
79
 
81
80
  The token header has these items:
82
- "alg": <string> # the algorithm used to sign the token (one of *HS256*, *HS51*', *RSA256*, *RSA512*)
83
- "typ": <string> # the token type (fixed to *JWT*
84
- "kid": <string> # a reference to the token type and the key to its location in the token database
81
+ "alg": <string> the algorithm used to sign the token (one of *HS256*, *HS51*', *RSA256*, *RSA512*)
82
+ "typ": <string> the token type (fixed to *JWT*
83
+ "kid": <string> a reference to the token type and the key to its location in the token database
85
84
 
86
85
  If issued by the local server, "kid" holds the key to the corresponding record in the token database,
87
86
  if starting with *A* for (*Access*) or *R* (for *Refresh*), followed an integer.
@@ -95,13 +94,12 @@ class JwtRegistry:
95
94
 
96
95
  def add_account(self,
97
96
  account_id: str,
98
- reference_url: str,
99
97
  claims: dict[str, Any],
100
98
  access_max_age: int,
101
99
  refresh_max_age: int,
102
100
  grace_interval: int | None,
103
101
  request_timeout: int | None,
104
- remote_provider: bool | None,
102
+ remote_url: str | None,
105
103
  logger: Logger = None) -> None:
106
104
  """
107
105
  Add to storage the parameters needed to produce and validate JWT tokens for *account_id*.
@@ -112,25 +110,23 @@ class JwtRegistry:
112
110
  If the token provider is remote, all claims are sent to it at token request time.
113
111
 
114
112
  :param account_id: the account identification
115
- :param reference_url: the reference URL (for remote providers, URL to obtain and validate the JWT tokens)
116
113
  :param claims: the JWT claimset, as key-value pairs
117
114
  :param access_max_age: access token duration, in seconds
118
115
  :param refresh_max_age: refresh token duration, in seconds
119
116
  :param grace_interval: time to wait for token to be valid, in seconds
120
117
  :param request_timeout: timeout for the requests to the reference URL (in seconds)
121
- :param remote_provider: whether the JWT provider is a remote server
118
+ :param remote_url: remote server's URL to obtain the JWT tokens from
122
119
  :param logger: optional logger
123
120
  """
124
121
  # build and store the access data for the account
125
122
  with self.access_lock:
126
123
  if account_id not in self.access_data:
127
124
  self.access_data[account_id] = {
128
- "reference-url": reference_url,
129
125
  "access-max-age": access_max_age,
130
126
  "refresh-max-age": refresh_max_age,
131
127
  "grace-interval": grace_interval,
132
128
  "request-timeout": request_timeout,
133
- "remote-provider": remote_provider,
129
+ "remote-url": remote_url,
134
130
  "claims": claims or {}
135
131
  }
136
132
  if logger:
@@ -195,8 +191,6 @@ class JwtRegistry:
195
191
  if not isinstance(nature, str) or \
196
192
  len(nature) != 1 or nature < "A" or nature > "Z":
197
193
  err_msg: str = f"Invalid nature '{nature}'"
198
- elif not isinstance(claims, dict) or "iss" not in claims:
199
- err_msg = f"invalid claims '{claims}'"
200
194
  elif not isinstance(duration, int) or duration < 60:
201
195
  err_msg = f"Invalid duration '{duration}'"
202
196
  if err_msg:
@@ -209,11 +203,14 @@ class JwtRegistry:
209
203
  logger=logger)
210
204
  # issue the token
211
205
  current_claims: dict[str, Any] = {}
206
+ iss: str = account_data["claims"].get("iss")
207
+ if iss:
208
+ current_claims["iss"] = iss
212
209
  if claims:
213
210
  current_claims.update(claims)
211
+
214
212
  current_claims["jti"] = str_random(size=32,
215
213
  chars=string.ascii_letters + string.digits)
216
- current_claims["iss"] = account_data.get("reference-url")
217
214
  current_claims["sub"] = account_id
218
215
  just_now: int = int(datetime.now(tz=timezone.utc).timestamp())
219
216
  current_claims["iat"] = just_now
@@ -240,7 +237,7 @@ class JwtRegistry:
240
237
  """
241
238
  Issue and return a JWT token pair associated with *account_id*.
242
239
 
243
- These claims are ignored, if specified in *account_claims*: *iat*, *iss*, *exp*, *jti*, *nbf*, and *sub*.
240
+ These claims are ignored, if specified in *account_claims*: *iat*, *exp*, *jti*, *nbf*, and *sub*.
244
241
  Other claims specified therein may supercede registered account-related claims.
245
242
 
246
243
  Structure of the return data:
@@ -264,20 +261,19 @@ class JwtRegistry:
264
261
  with (self.access_lock):
265
262
  account_data: dict[str, Any] = self.__get_account_data(account_id=account_id,
266
263
  logger=logger)
267
- current_claims: dict[str, Any] = account_data.get("claims").copy()
264
+ current_claims: dict[str, Any] = account_data["claims"].copy()
268
265
  if account_claims:
269
266
  current_claims.update(account_claims)
270
267
  current_claims["jti"] = str_random(size=32,
271
268
  chars=string.ascii_letters + string.digits)
272
269
  current_claims["sub"] = account_id
273
- current_claims["iss"] = account_data.get("reference-url")
274
270
  errors: list[str] = []
275
271
 
276
272
  # where is the JWT service provider ?
277
- if account_data.get("remote-provider"):
273
+ if account_data.get("remote-url"):
278
274
  # JWT service is being provided by a remote server
279
275
  result = _jwt_request_token(errors=errors,
280
- reference_url=current_claims.get("iss"),
276
+ remote_url=account_data.get("remote-url"),
281
277
  claims=current_claims,
282
278
  timeout=account_data.get("request-timeout"),
283
279
  logger=logger)
@@ -371,7 +367,7 @@ class JwtRegistry:
371
367
 
372
368
 
373
369
  def _jwt_request_token(errors: list[str],
374
- reference_url: str,
370
+ remote_url: str,
375
371
  claims: dict[str, Any],
376
372
  timeout: int = None,
377
373
  logger: Logger = None) -> dict[str, Any]:
@@ -389,7 +385,7 @@ def _jwt_request_token(errors: list[str],
389
385
  of the provider issuing the JWT token.
390
386
 
391
387
  :param errors: incidental errors
392
- :param reference_url: the reference URL for obtaining JWT tokens
388
+ :param remote_url: the reference URL for obtaining JWT tokens
393
389
  :param claims: the JWT claimset, as expected by the issuing server
394
390
  :param timeout: request timeout, in seconds (defaults to *None*)
395
391
  :param logger: optional logger
@@ -399,9 +395,9 @@ def _jwt_request_token(errors: list[str],
399
395
 
400
396
  # request the JWT token
401
397
  if logger:
402
- logger.debug(f"POST request JWT token to '{reference_url}'")
398
+ logger.debug(f"POST request JWT token to '{remote_url}'")
403
399
  response: Response = requests.post(
404
- url=reference_url,
400
+ url=remote_url,
405
401
  json=claims,
406
402
  timeout=timeout
407
403
  )
@@ -414,7 +410,7 @@ def _jwt_request_token(errors: list[str],
414
410
  logger.debug(f"JWT token obtained: {result}")
415
411
  else:
416
412
  # no, report the problem
417
- err_msg: str = f"POST request to '{reference_url}' failed: {response.reason}"
413
+ err_msg: str = f"POST request to '{remote_url}' failed: {response.reason}"
418
414
  if response.text:
419
415
  err_msg += f" - {response.text}"
420
416
  if logger:
File without changes
File without changes
File without changes