pypomes-jwt 0.6.1__tar.gz → 0.6.3__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.
- {pypomes_jwt-0.6.1 → pypomes_jwt-0.6.3}/PKG-INFO +2 -2
- {pypomes_jwt-0.6.1 → pypomes_jwt-0.6.3}/pyproject.toml +3 -3
- {pypomes_jwt-0.6.1 → pypomes_jwt-0.6.3}/src/pypomes_jwt/jwt_data.py +93 -69
- {pypomes_jwt-0.6.1 → pypomes_jwt-0.6.3}/src/pypomes_jwt/jwt_pomes.py +39 -21
- {pypomes_jwt-0.6.1 → pypomes_jwt-0.6.3}/.gitignore +0 -0
- {pypomes_jwt-0.6.1 → pypomes_jwt-0.6.3}/LICENSE +0 -0
- {pypomes_jwt-0.6.1 → pypomes_jwt-0.6.3}/README.md +0 -0
- {pypomes_jwt-0.6.1 → pypomes_jwt-0.6.3}/src/__init__.py +0 -0
- {pypomes_jwt-0.6.1 → pypomes_jwt-0.6.3}/src/pypomes_jwt/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pypomes_jwt
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.3
|
|
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
|
|
@@ -12,4 +12,4 @@ Classifier: Programming Language :: Python :: 3
|
|
|
12
12
|
Requires-Python: >=3.12
|
|
13
13
|
Requires-Dist: cryptography>=44.0.1
|
|
14
14
|
Requires-Dist: pyjwt>=2.10.1
|
|
15
|
-
Requires-Dist: pypomes-core>=1.7.
|
|
15
|
+
Requires-Dist: pypomes-core>=1.7.8
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
[build-system]
|
|
2
2
|
requires = [
|
|
3
|
-
"hatchling>=1.
|
|
3
|
+
"hatchling>=1.27.0"
|
|
4
4
|
]
|
|
5
5
|
build-backend = "hatchling.build"
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "pypomes_jwt"
|
|
9
|
-
version = "0.6.
|
|
9
|
+
version = "0.6.3"
|
|
10
10
|
authors = [
|
|
11
11
|
{ name="GT Nunes", email="wisecoder01@gmail.com" }
|
|
12
12
|
]
|
|
@@ -21,7 +21,7 @@ classifiers = [
|
|
|
21
21
|
dependencies = [
|
|
22
22
|
"PyJWT>=2.10.1",
|
|
23
23
|
"cryptography>=44.0.1",
|
|
24
|
-
"pypomes_core>=1.7.
|
|
24
|
+
"pypomes_core>=1.7.8"
|
|
25
25
|
]
|
|
26
26
|
|
|
27
27
|
[project.urls]
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import jwt
|
|
2
2
|
import requests
|
|
3
|
-
from datetime import datetime,
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
4
|
from jwt.exceptions import InvalidTokenError
|
|
5
5
|
from logging import Logger
|
|
6
|
+
from pypomes_core import str_random
|
|
6
7
|
from requests import Response
|
|
7
8
|
from threading import Lock
|
|
8
9
|
from typing import Any, Literal
|
|
@@ -17,6 +18,17 @@ class JwtData:
|
|
|
17
18
|
- access_data: list with dictionaries holding the JWT token data:
|
|
18
19
|
[
|
|
19
20
|
{
|
|
21
|
+
"control-data": { # control data
|
|
22
|
+
"remote-provider": <bool>, # whether the JWT provider is a remote server
|
|
23
|
+
"access-token": <jwt-token>, # access token
|
|
24
|
+
"algorithm": <string>, # HS256, HS512, RSA256, RSA512
|
|
25
|
+
"request-timeout": <int>, # in seconds - defaults to no timeout
|
|
26
|
+
"access-max-age": <int>, # in seconds - defaults to JWT_ACCESS_MAX_AGE
|
|
27
|
+
"refresh-exp": <timestamp>, # expiration time for the refresh operation
|
|
28
|
+
"hs-secret-key": <bytes>, # HS secret key
|
|
29
|
+
"rsa-private-key": <bytes>, # RSA private key
|
|
30
|
+
"rsa-public-key": <bytes>, # RSA public key
|
|
31
|
+
},
|
|
20
32
|
"reserved-claims": { # reserved claims
|
|
21
33
|
"exp": <timestamp>, # expiration time
|
|
22
34
|
"iat": <timestamp> # issued at
|
|
@@ -38,17 +50,6 @@ class JwtData:
|
|
|
38
50
|
"<custom-claim-key-1>": "<custom-claim-value-1>",
|
|
39
51
|
...
|
|
40
52
|
"<custom-claim-key-n>": "<custom-claim-value-n>"
|
|
41
|
-
},
|
|
42
|
-
"control-data": { # control data
|
|
43
|
-
"remote-provider": <bool>, # whether the JWT provider is a remote server
|
|
44
|
-
"access-token": <jwt-token>, # access token
|
|
45
|
-
"algorithm": <string>, # HS256, HS512, RSA256, RSA512
|
|
46
|
-
"request-timeout": <int>, # in seconds - defaults to no timeout
|
|
47
|
-
"access-max-age": <int>, # in seconds - defaults to JWT_ACCESS_MAX_AGE
|
|
48
|
-
"refresh-exp": <timestamp>, # expiration time for the refresh operation
|
|
49
|
-
"secret-key": <bytes>, # HS secret key
|
|
50
|
-
"private-key": <bytes>, # RSA private key
|
|
51
|
-
"public-key": <bytes>, # RSA public key
|
|
52
53
|
}
|
|
53
54
|
},
|
|
54
55
|
...
|
|
@@ -68,9 +69,9 @@ class JwtData:
|
|
|
68
69
|
algorithm: Literal["HS256", "HS512", "RSA256", "RSA512"],
|
|
69
70
|
access_max_age: int,
|
|
70
71
|
refresh_max_age: int,
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
hs_secret_key: bytes,
|
|
73
|
+
rsa_private_key: bytes,
|
|
74
|
+
rsa_public_key: bytes,
|
|
74
75
|
request_timeout: int,
|
|
75
76
|
remote_provider: bool,
|
|
76
77
|
logger: Logger = None) -> None:
|
|
@@ -78,11 +79,11 @@ class JwtData:
|
|
|
78
79
|
Add to storage the parameters needed to produce and validate JWT tokens for *account_id*.
|
|
79
80
|
|
|
80
81
|
The parameter *claims* may contain public and custom claims. Currently, the public claims supported
|
|
81
|
-
are *birthdate*, *email*, *gender*, *name*, and *roles*. Everything else
|
|
82
|
-
claims, and
|
|
82
|
+
are *birthdate*, *email*, *gender*, *name*, and *roles*. Everything else is considered to be custom
|
|
83
|
+
claims, and sent to the remote JWT provided, if applicable.
|
|
83
84
|
|
|
84
85
|
Presently, the *refresh_max_age* data is not relevant, as the authorization parameters in *claims*
|
|
85
|
-
(typically, an acess-key/secret-key pair), have been previously validated elsewhere.
|
|
86
|
+
(typically, an acess-key/hs-secret-key pair), have been previously validated elsewhere.
|
|
86
87
|
This situation might change in the future.
|
|
87
88
|
|
|
88
89
|
:param account_id: the account identification
|
|
@@ -91,28 +92,28 @@ class JwtData:
|
|
|
91
92
|
:param algorithm: the algorithm used to sign the token with
|
|
92
93
|
:param access_max_age: token duration (in seconds)
|
|
93
94
|
:param refresh_max_age: duration for the refresh operation (in seconds)
|
|
94
|
-
:param
|
|
95
|
-
:param
|
|
96
|
-
:param
|
|
95
|
+
:param hs_secret_key: secret key for HS authentication
|
|
96
|
+
:param rsa_private_key: private key for RSA authentication
|
|
97
|
+
:param rsa_public_key: public key for RSA authentication
|
|
97
98
|
:param request_timeout: timeout for the requests to the reference URL
|
|
98
99
|
:param remote_provider: whether the JWT provider is a remote server
|
|
99
100
|
:param logger: optional logger
|
|
100
101
|
"""
|
|
101
102
|
# Do the access data already exist ?
|
|
102
|
-
if not self.
|
|
103
|
+
if not self.get_access_data(account_id=account_id):
|
|
103
104
|
# no, build control data
|
|
104
105
|
control_data: dict[str, Any] = {
|
|
105
106
|
"algorithm": algorithm,
|
|
106
107
|
"access-max-age": access_max_age,
|
|
107
108
|
"request-timeout": request_timeout,
|
|
108
109
|
"remote-provider": remote_provider,
|
|
109
|
-
"refresh-exp": datetime.now(tz=timezone.utc) +
|
|
110
|
+
"refresh-exp": datetime.now(tz=timezone.utc).timestamp() + refresh_max_age
|
|
110
111
|
}
|
|
111
112
|
if algorithm in ["HS256", "HS512"]:
|
|
112
|
-
control_data["secret-key"] =
|
|
113
|
+
control_data["hs-secret-key"] = hs_secret_key
|
|
113
114
|
else:
|
|
114
|
-
control_data["private-key"] =
|
|
115
|
-
control_data["public-key"] =
|
|
115
|
+
control_data["rsa-private-key"] = rsa_private_key
|
|
116
|
+
control_data["rsa-public-key"] = rsa_public_key
|
|
116
117
|
|
|
117
118
|
# build claims
|
|
118
119
|
reserved_claims: dict[str, Any] = {
|
|
@@ -120,7 +121,7 @@ class JwtData:
|
|
|
120
121
|
"iss": reference_url,
|
|
121
122
|
"exp": "<numeric-UTC-datetime>",
|
|
122
123
|
"iat": "<numeric-UTC-datetime>",
|
|
123
|
-
"jti": "<jwt-id",
|
|
124
|
+
"jti": "<jwt-id>",
|
|
124
125
|
}
|
|
125
126
|
custom_claims: dict[str, Any] = {}
|
|
126
127
|
public_claims: dict[str, Any] = {}
|
|
@@ -153,8 +154,8 @@ class JwtData:
|
|
|
153
154
|
:param logger: optional logger
|
|
154
155
|
"""
|
|
155
156
|
# obtain the access data item in storage
|
|
156
|
-
item_data: dict[str, dict[str, Any]] = self.
|
|
157
|
-
|
|
157
|
+
item_data: dict[str, dict[str, Any]] = self.get_access_data(account_id=account_id,
|
|
158
|
+
logger=logger)
|
|
158
159
|
if item_data:
|
|
159
160
|
with self.access_lock:
|
|
160
161
|
self.access_data.remove(item_data)
|
|
@@ -172,6 +173,7 @@ class JwtData:
|
|
|
172
173
|
Structure of the return data:
|
|
173
174
|
{
|
|
174
175
|
"access_token": <jwt-token>,
|
|
176
|
+
"created_in": <timestamp>,
|
|
175
177
|
"expires_in": <seconds-to-expiration>
|
|
176
178
|
}
|
|
177
179
|
|
|
@@ -195,8 +197,8 @@ class JwtData:
|
|
|
195
197
|
result: dict[str, Any]
|
|
196
198
|
|
|
197
199
|
# obtain the item in storage
|
|
198
|
-
item_data: dict[str, Any] = self.
|
|
199
|
-
|
|
200
|
+
item_data: dict[str, Any] = self.get_access_data(account_id=account_id,
|
|
201
|
+
logger=logger)
|
|
200
202
|
# was the JWT data obtained ?
|
|
201
203
|
if item_data:
|
|
202
204
|
# yes, proceed
|
|
@@ -207,40 +209,51 @@ class JwtData:
|
|
|
207
209
|
|
|
208
210
|
# obtain a new token, if the current token has expired
|
|
209
211
|
if just_now > reserved_claims.get("exp"):
|
|
210
|
-
# where is the
|
|
212
|
+
# where is the JWT service provider ?
|
|
211
213
|
if control_data.get("remote-provider"):
|
|
212
214
|
# JWT service is being provided by a remote server
|
|
213
215
|
errors: list[str] = []
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
216
|
+
# Structure of the return data:
|
|
217
|
+
# {
|
|
218
|
+
# "access_token": <jwt-token>,
|
|
219
|
+
# "created_in": <timestamp>,
|
|
220
|
+
# "expires_in": <seconds-to-expiration>,
|
|
221
|
+
# ...
|
|
222
|
+
# }
|
|
223
|
+
reply: dict[str, Any] = jwt_request_token(errors=errors,
|
|
224
|
+
reference_url=reserved_claims.get("iss"),
|
|
225
|
+
claims=custom_claims,
|
|
226
|
+
timeout=control_data.get("request-timeout"),
|
|
227
|
+
logger=logger)
|
|
228
|
+
if reply:
|
|
220
229
|
with self.access_lock:
|
|
221
|
-
control_data["access-token"] =
|
|
222
|
-
|
|
223
|
-
reserved_claims["
|
|
230
|
+
control_data["access-token"] = reply.get("access_token")
|
|
231
|
+
reserved_claims["jti"] = str_random(size=16)
|
|
232
|
+
reserved_claims["iat"] = reply.get("created_in")
|
|
233
|
+
reserved_claims["exp"] = reply.get("created_in") + reply.get("expires_in")
|
|
224
234
|
else:
|
|
225
235
|
raise RuntimeError(" - ".join(errors))
|
|
226
236
|
else:
|
|
227
237
|
# JWT service is being provided locally
|
|
228
|
-
reserved_claims["iat"] = just_now
|
|
229
|
-
reserved_claims["exp"] = just_now + control_data.get("access-max-age")
|
|
230
238
|
claims: dict[str, Any] = item_data.get("public-claims").copy()
|
|
231
|
-
claims.update(reserved_claims)
|
|
232
|
-
claims.update(custom_claims)
|
|
239
|
+
claims.update(m=reserved_claims)
|
|
240
|
+
claims.update(m=custom_claims)
|
|
233
241
|
# may raise an exception
|
|
234
242
|
token: str = jwt.encode(payload=claims,
|
|
235
|
-
key=control_data.get("secret-key") or
|
|
243
|
+
key=(control_data.get("hs-secret-key") or
|
|
244
|
+
control_data.get("rsa-private-key")),
|
|
236
245
|
algorithm=control_data.get("algorithm"))
|
|
237
246
|
with self.access_lock:
|
|
247
|
+
reserved_claims["jti"] = str_random(size=16)
|
|
248
|
+
reserved_claims["iat"] = just_now
|
|
249
|
+
reserved_claims["exp"] = just_now + control_data.get("access-max-age")
|
|
238
250
|
control_data["access-token"] = token
|
|
239
251
|
|
|
240
252
|
# return the token
|
|
241
253
|
result = {
|
|
242
254
|
"access_token": control_data.get("access-token"),
|
|
243
|
-
"
|
|
255
|
+
"created_in": reserved_claims.get("iat"),
|
|
256
|
+
"expires_in": reserved_claims.get("exp") - reserved_claims.get("iat")
|
|
244
257
|
}
|
|
245
258
|
else:
|
|
246
259
|
# JWT access data not found
|
|
@@ -263,37 +276,46 @@ class JwtData:
|
|
|
263
276
|
:raises InvalidTokenError: token is not valid
|
|
264
277
|
:raises ExpiredSignatureError: token has expired
|
|
265
278
|
"""
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
with self.access_lock:
|
|
269
|
-
for item_data in self.access_data:
|
|
270
|
-
control_data: dict[str, Any] = item_data.get("control-data")
|
|
271
|
-
if token == control_data.get("access-token"):
|
|
272
|
-
algorithm = control_data.get("algorithm")
|
|
273
|
-
key = control_data.get("public-key") or control_data.get("secret-key")
|
|
274
|
-
break
|
|
275
|
-
|
|
276
|
-
if not algorithm or not key:
|
|
277
|
-
raise InvalidTokenError("JWT token is not valid")
|
|
279
|
+
# declare the return variable
|
|
280
|
+
result: dict[str, Any]
|
|
278
281
|
|
|
279
282
|
if logger:
|
|
280
283
|
logger.debug(msg=f"Retrieve claims for JWT token '{token}'")
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
+
|
|
285
|
+
control_data: dict[str, Any] = self.get_access_data(access_token=token,
|
|
286
|
+
logger=logger)
|
|
287
|
+
if control_data:
|
|
288
|
+
if control_data.get("remote-provider"):
|
|
289
|
+
# provider is remote
|
|
290
|
+
result = control_data.get("custom-claims")
|
|
291
|
+
else:
|
|
292
|
+
# may raise InvalidTokenError or ExpiredSignatureError
|
|
293
|
+
result = jwt.decode(jwt=token,
|
|
294
|
+
key=(control_data.get("hs-secret-key") or
|
|
295
|
+
control_data.get("rsa-public-key")),
|
|
296
|
+
algorithms=[control_data.get("algorithm")])
|
|
297
|
+
else:
|
|
298
|
+
raise InvalidTokenError("JWT token is not valid")
|
|
299
|
+
|
|
284
300
|
if logger:
|
|
285
301
|
logger.debug(f"Retrieved claims for JWT token '{token}': {result}")
|
|
286
302
|
|
|
287
303
|
return result
|
|
288
304
|
|
|
289
|
-
def
|
|
290
|
-
|
|
291
|
-
|
|
305
|
+
def get_access_data(self,
|
|
306
|
+
account_id: str = None,
|
|
307
|
+
access_token: str = None,
|
|
308
|
+
logger: Logger = None) -> dict[str, dict[str, Any]]:
|
|
292
309
|
# noinspection HttpUrlsUsage
|
|
293
310
|
"""
|
|
294
|
-
Retrieve and return the access data in storage for *account_id*.
|
|
311
|
+
Retrieve and return the access data in storage for *account_id*, or optionally, for *access_token*.
|
|
312
|
+
|
|
313
|
+
Either *account_id* or *access_token* must be provided, the former having precedence over the later.
|
|
314
|
+
Note that, whereas *account_id* uniquely identifies an access dataset, *access_token* might not,
|
|
315
|
+
and thus, the first dataset associated with it would be returned.
|
|
295
316
|
|
|
296
317
|
:param account_id: the account identification
|
|
318
|
+
:param access_token: the access token
|
|
297
319
|
:param logger: optional logger
|
|
298
320
|
:return: the corresponding item in storage, or *None* if not found
|
|
299
321
|
"""
|
|
@@ -301,11 +323,13 @@ class JwtData:
|
|
|
301
323
|
result: dict[str, dict[str, Any]] | None = None
|
|
302
324
|
|
|
303
325
|
if logger:
|
|
304
|
-
|
|
326
|
+
target: str = f"account id '{account_id}'" if account_id else f"token '{access_token}'"
|
|
327
|
+
logger.debug(f"Retrieve access data for {target}")
|
|
305
328
|
# retrieve the data
|
|
306
329
|
with self.access_lock:
|
|
307
330
|
for item_data in self.access_data:
|
|
308
|
-
if account_id == item_data.get("reserved-claims").get("sub")
|
|
331
|
+
if (account_id and account_id == item_data.get("reserved-claims").get("sub")) or \
|
|
332
|
+
(access_token and access_token == item_data.get("control-data").get("access-token")):
|
|
309
333
|
result = item_data
|
|
310
334
|
break
|
|
311
335
|
if logger:
|
|
@@ -320,7 +344,7 @@ def jwt_request_token(errors: list[str],
|
|
|
320
344
|
timeout: int = None,
|
|
321
345
|
logger: Logger = None) -> dict[str, Any]:
|
|
322
346
|
"""
|
|
323
|
-
Obtain and return the JWT token
|
|
347
|
+
Obtain and return the JWT token from *reference_url*, along with its duration.
|
|
324
348
|
|
|
325
349
|
Expected structure of the return data:
|
|
326
350
|
{
|
|
@@ -2,6 +2,7 @@ import contextlib
|
|
|
2
2
|
from cryptography.hazmat.primitives import serialization
|
|
3
3
|
from cryptography.hazmat.primitives.asymmetric import rsa
|
|
4
4
|
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey, RSAPublicKey
|
|
5
|
+
from datetime import datetime
|
|
5
6
|
from flask import Request, Response, request, jsonify
|
|
6
7
|
from logging import Logger
|
|
7
8
|
from pypomes_core import APP_PREFIX, env_get_str, env_get_bytes, env_get_int
|
|
@@ -64,7 +65,7 @@ def jwt_assert_access(account_id: str) -> bool:
|
|
|
64
65
|
:param account_id: the account identification
|
|
65
66
|
:return: *True* if access data exists for *account_id*, *False* otherwise
|
|
66
67
|
"""
|
|
67
|
-
return __jwt_data.
|
|
68
|
+
return __jwt_data.get_access_data(account_id=account_id) is not None
|
|
68
69
|
|
|
69
70
|
|
|
70
71
|
def jwt_set_access(account_id: str,
|
|
@@ -113,9 +114,9 @@ def jwt_set_access(account_id: str,
|
|
|
113
114
|
algorithm=algorithm,
|
|
114
115
|
access_max_age=access_max_age,
|
|
115
116
|
refresh_max_age=refresh_max_age,
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
hs_secret_key=secret_key,
|
|
118
|
+
rsa_private_key=private_key,
|
|
119
|
+
rsa_public_key=public_key,
|
|
119
120
|
request_timeout=request_timeout,
|
|
120
121
|
remote_provider=remote_provider,
|
|
121
122
|
logger=logger)
|
|
@@ -176,6 +177,7 @@ def jwt_get_token_data(errors: list[str],
|
|
|
176
177
|
Structure of the return data:
|
|
177
178
|
{
|
|
178
179
|
"access_token": <jwt-token>,
|
|
180
|
+
"created_in": <timestamp>,
|
|
179
181
|
"expires_in": <seconds-to-expiration>
|
|
180
182
|
}
|
|
181
183
|
|
|
@@ -236,13 +238,14 @@ def jwt_verify_request(request: Request,
|
|
|
236
238
|
|
|
237
239
|
:param request: the request to be verified
|
|
238
240
|
:param logger: optional logger
|
|
239
|
-
:return:
|
|
241
|
+
:return: *None* if the request is valid, otherwise a *Response* object reporting the error
|
|
240
242
|
"""
|
|
241
243
|
# initialize the return variable
|
|
242
244
|
result: Response | None = None
|
|
243
|
-
|
|
245
|
+
|
|
244
246
|
if logger:
|
|
245
247
|
logger.debug(msg="Validate a JWT token")
|
|
248
|
+
err_msg: str | None = None
|
|
246
249
|
|
|
247
250
|
# retrieve the authorization from the request header
|
|
248
251
|
auth_header: str = request.headers.get("Authorization")
|
|
@@ -253,20 +256,34 @@ def jwt_verify_request(request: Request,
|
|
|
253
256
|
token: str = auth_header.split(" ")[1]
|
|
254
257
|
if logger:
|
|
255
258
|
logger.debug(msg=f"Token is '{token}'")
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
259
|
+
# retrieve the reference access data
|
|
260
|
+
access_data: dict[str, Any] = __jwt_data.get_access_data(access_token=token)
|
|
261
|
+
if access_data:
|
|
262
|
+
control_data: dict[str, Any] = access_data.get("control-data")
|
|
263
|
+
if control_data.get("remote-provider"):
|
|
264
|
+
# JWT provider is remote
|
|
265
|
+
if datetime.now().timestamp() > access_data.get("reserved-claims").get("exp"):
|
|
266
|
+
err_msg = "Token has expired"
|
|
267
|
+
else:
|
|
268
|
+
# JWT was locally provided
|
|
269
|
+
try:
|
|
270
|
+
jwt_validate_token(token=token,
|
|
271
|
+
key=(control_data.get("hs-secret-key") or
|
|
272
|
+
control_data.get("rsa-public-key")),
|
|
273
|
+
algorithm=control_data.get("algorithm"))
|
|
274
|
+
except Exception as e:
|
|
275
|
+
# validation failed
|
|
276
|
+
err_msg = str(e)
|
|
277
|
+
else:
|
|
278
|
+
err_msg = "No access data found for token"
|
|
266
279
|
else:
|
|
267
|
-
# no, report the error
|
|
280
|
+
# no 'Bearer' found, report the error
|
|
281
|
+
err_msg = "Request header has no 'Bearer' data"
|
|
282
|
+
|
|
283
|
+
# log the error and deny the authorization
|
|
284
|
+
if err_msg:
|
|
268
285
|
if logger:
|
|
269
|
-
logger.error(msg=
|
|
286
|
+
logger.error(msg=err_msg)
|
|
270
287
|
result = Response(response="Authorization failed",
|
|
271
288
|
status=401)
|
|
272
289
|
|
|
@@ -293,13 +310,14 @@ def jwt_service(account_id: str = None,
|
|
|
293
310
|
Structure of the return data:
|
|
294
311
|
{
|
|
295
312
|
"access_token": <jwt-token>,
|
|
313
|
+
"created_in": <timestamp>,
|
|
296
314
|
"expires_in": <seconds-to-expiration>
|
|
297
315
|
}
|
|
298
316
|
|
|
299
317
|
:param account_id: the account identification, alternatively passed in JSON
|
|
300
318
|
:param service_params: the optional JSON containing the request parameters (defaults to JSON in body)
|
|
301
319
|
:param logger: optional logger
|
|
302
|
-
:return: the requested JWT token,
|
|
320
|
+
:return: a *Response* containing the requested JWT token and its duration, or reporting an error
|
|
303
321
|
"""
|
|
304
322
|
# declare the return variable
|
|
305
323
|
result: Response
|
|
@@ -324,8 +342,8 @@ def jwt_service(account_id: str = None,
|
|
|
324
342
|
# yes, proceed
|
|
325
343
|
if logger:
|
|
326
344
|
logger.debug(msg=f"Account identification is '{account_id}'")
|
|
327
|
-
item_data: dict[str, dict[str, Any]] = __jwt_data.
|
|
328
|
-
|
|
345
|
+
item_data: dict[str, dict[str, Any]] = __jwt_data.get_access_data(account_id=account_id,
|
|
346
|
+
logger=logger) or {}
|
|
329
347
|
custom_claims: dict[str, Any] = item_data.get("custom-claims").copy()
|
|
330
348
|
for key, value in params.items():
|
|
331
349
|
custom_claims[key] = value
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|