pypomes-jwt 1.3.7__py3-none-any.whl → 1.3.9__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 +2 -7
- pypomes_jwt/jwt_config.py +1 -1
- pypomes_jwt/jwt_pomes.py +14 -13
- {pypomes_jwt-1.3.7.dist-info → pypomes_jwt-1.3.9.dist-info}/METADATA +4 -3
- pypomes_jwt-1.3.9.dist-info/RECORD +8 -0
- pypomes_jwt/jwt_providers.py +0 -137
- pypomes_jwt-1.3.7.dist-info/RECORD +0 -9
- {pypomes_jwt-1.3.7.dist-info → pypomes_jwt-1.3.9.dist-info}/WHEEL +0 -0
- {pypomes_jwt-1.3.7.dist-info → pypomes_jwt-1.3.9.dist-info}/licenses/LICENSE +0 -0
pypomes_jwt/__init__.py
CHANGED
|
@@ -7,20 +7,15 @@ from .jwt_pomes import (
|
|
|
7
7
|
jwt_issue_token, jwt_issue_tokens, jwt_refresh_tokens,
|
|
8
8
|
jwt_get_claims, jwt_validate_token, jwt_revoke_token
|
|
9
9
|
)
|
|
10
|
-
from .jwt_providers import (
|
|
11
|
-
provider_register, provider_get_token
|
|
12
|
-
)
|
|
13
10
|
|
|
14
11
|
__all__ = [
|
|
15
|
-
#
|
|
12
|
+
# jwt_config
|
|
16
13
|
"JwtConfig", "JwtDbConfig", "JwtAlgorithm",
|
|
17
14
|
# jwt_pomes
|
|
18
15
|
"jwt_needed", "jwt_verify_request",
|
|
19
16
|
"jwt_assert_account", "jwt_set_account", "jwt_remove_account",
|
|
20
17
|
"jwt_issue_token", "jwt_issue_tokens", "jwt_refresh_tokens",
|
|
21
|
-
"jwt_get_claims", "jwt_validate_token", "jwt_revoke_token"
|
|
22
|
-
# jwt_providers
|
|
23
|
-
"provider_register", "provider_get_token"
|
|
18
|
+
"jwt_get_claims", "jwt_validate_token", "jwt_revoke_token"
|
|
24
19
|
]
|
|
25
20
|
|
|
26
21
|
from importlib.metadata import version
|
pypomes_jwt/jwt_config.py
CHANGED
|
@@ -66,7 +66,7 @@ del _encoding_key
|
|
|
66
66
|
del _default_algorithm
|
|
67
67
|
|
|
68
68
|
|
|
69
|
-
# database access is not be necessary, if handling
|
|
69
|
+
# database access is not be necessary, if only handling externally provided JWT tokens
|
|
70
70
|
class JwtDbConfig(StrEnum):
|
|
71
71
|
"""
|
|
72
72
|
Parameters for JWT database connection.
|
pypomes_jwt/jwt_pomes.py
CHANGED
|
@@ -52,7 +52,7 @@ def jwt_verify_request(request: Request) -> Response:
|
|
|
52
52
|
# validate the authorization token
|
|
53
53
|
bad_token: bool = True
|
|
54
54
|
if auth_header and auth_header.startswith("Bearer "):
|
|
55
|
-
#
|
|
55
|
+
# extract and validate the JWT access token
|
|
56
56
|
token: str = auth_header.split(" ")[1]
|
|
57
57
|
claims: dict[str, Any] = jwt_validate_token(token=token,
|
|
58
58
|
nature="A")
|
|
@@ -224,14 +224,17 @@ def jwt_validate_token(token: str,
|
|
|
224
224
|
# InvalidIssuedAtError: 'iat' claim is non-numeric
|
|
225
225
|
# MissingRequiredClaimError: a required claim is not contained in the claimset
|
|
226
226
|
payload: dict[str, Any] = jwt.decode(jwt=token,
|
|
227
|
+
key=token_decoder,
|
|
228
|
+
algorithms=token_alg,
|
|
227
229
|
options={
|
|
228
|
-
"
|
|
230
|
+
"require": ["iat", "iss", "exp", "sub"],
|
|
231
|
+
"verify_aud": False,
|
|
229
232
|
"verify_exp": True,
|
|
230
|
-
"
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
233
|
+
"verify_iat": True,
|
|
234
|
+
"verify_iss": False,
|
|
235
|
+
"verify_nbf": True,
|
|
236
|
+
"verify_signature": True
|
|
237
|
+
})
|
|
235
238
|
if account_id and payload.get("sub") != account_id:
|
|
236
239
|
if logger:
|
|
237
240
|
logger.error(msg=f"Token does not belong to account '{account_id}'")
|
|
@@ -505,7 +508,7 @@ def jwt_refresh_tokens(account_id: str,
|
|
|
505
508
|
|
|
506
509
|
def jwt_get_claims(token: str,
|
|
507
510
|
errors: list[str] = None,
|
|
508
|
-
logger: Logger = None) -> dict[str, Any] | None:
|
|
511
|
+
logger: Logger = None) -> dict[str, dict[str, Any]] | None:
|
|
509
512
|
"""
|
|
510
513
|
Retrieve the claims set of a JWT *token*.
|
|
511
514
|
|
|
@@ -520,8 +523,6 @@ def jwt_get_claims(token: str,
|
|
|
520
523
|
"kid": "A1234"
|
|
521
524
|
},
|
|
522
525
|
"payload": {
|
|
523
|
-
"valid-from": <YYYY-MM-DDThh:mm:ss+00:00>
|
|
524
|
-
"valid-until": <YYYY-MM-DDThh:mm:ss+00:00>
|
|
525
526
|
"birthdate": "1980-01-01",
|
|
526
527
|
"email": "jdoe@mail.com",
|
|
527
528
|
"exp": 1516640454,
|
|
@@ -539,13 +540,13 @@ def jwt_get_claims(token: str,
|
|
|
539
540
|
}
|
|
540
541
|
}
|
|
541
542
|
|
|
542
|
-
:param token: the token
|
|
543
|
+
:param token: the reference token
|
|
543
544
|
:param errors: incidental error messages
|
|
544
545
|
:param logger: optional logger
|
|
545
546
|
:return: the token's claimset, or *None* if error
|
|
546
547
|
"""
|
|
547
548
|
# initialize the return variable
|
|
548
|
-
result: dict[str, Any] | None = None
|
|
549
|
+
result: dict[str, dict[str, Any]] | None = None
|
|
549
550
|
|
|
550
551
|
if logger:
|
|
551
552
|
logger.debug(msg="Retrieve claims for token")
|
|
@@ -562,7 +563,7 @@ def jwt_get_claims(token: str,
|
|
|
562
563
|
exc_err: str = exc_format(exc=e,
|
|
563
564
|
exc_info=sys.exc_info())
|
|
564
565
|
if logger:
|
|
565
|
-
logger.error(msg=f"Error retrieving the token's
|
|
566
|
+
logger.error(msg=f"Error retrieving the token's claims: {exc_err}")
|
|
566
567
|
if isinstance(errors, list):
|
|
567
568
|
errors.append(exc_err)
|
|
568
569
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pypomes_jwt
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.9
|
|
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
|
|
@@ -10,7 +10,8 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
10
10
|
Classifier: Operating System :: OS Independent
|
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
|
12
12
|
Requires-Python: >=3.12
|
|
13
|
-
Requires-Dist: cryptography>=46.0.
|
|
13
|
+
Requires-Dist: cryptography>=46.0.3
|
|
14
14
|
Requires-Dist: flask>=3.1.2
|
|
15
15
|
Requires-Dist: pyjwt>=2.10.1
|
|
16
|
-
Requires-Dist: pypomes-core>=2.7.
|
|
16
|
+
Requires-Dist: pypomes-core>=2.7.8
|
|
17
|
+
Requires-Dist: pypomes-db>=2.8.1
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
pypomes_jwt/__init__.py,sha256=esLvNt3Vr4WiZlx1lqKbLIXpDhdBFjqhqKUM6laSwg4,820
|
|
2
|
+
pypomes_jwt/jwt_config.py,sha256=ypr7BCRp1slJ503iyVmma-ljbaZAnbk_qpZKNRjD5CI,4026
|
|
3
|
+
pypomes_jwt/jwt_pomes.py,sha256=sje_1kTSp4I8MEw7jlwYHrEX2JB7a8iEqwt4curfvqc,23659
|
|
4
|
+
pypomes_jwt/jwt_registry.py,sha256=ypBEoL0I2F08sR2G2VO9wXxVeE252lNzjIAC3FGORhA,22631
|
|
5
|
+
pypomes_jwt-1.3.9.dist-info/METADATA,sha256=Rf63Qn0O4FSB-IHmnjZDgI39_Mq5_VRreon8QKYhgpA,660
|
|
6
|
+
pypomes_jwt-1.3.9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
7
|
+
pypomes_jwt-1.3.9.dist-info/licenses/LICENSE,sha256=NdakochSXm_H_-DSL_x2JlRCkYikj3snYYvTwgR5d_c,1086
|
|
8
|
+
pypomes_jwt-1.3.9.dist-info/RECORD,,
|
pypomes_jwt/jwt_providers.py
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import requests
|
|
2
|
-
import sys
|
|
3
|
-
from base64 import b64encode
|
|
4
|
-
from datetime import datetime
|
|
5
|
-
from logging import Logger
|
|
6
|
-
from pypomes_core import TZ_LOCAL, exc_format
|
|
7
|
-
from typing import Any
|
|
8
|
-
|
|
9
|
-
# structure:
|
|
10
|
-
# {
|
|
11
|
-
# <provider-id>: {
|
|
12
|
-
# "url": <strl>,
|
|
13
|
-
# "user": <str>,
|
|
14
|
-
# "pwd": <str>,
|
|
15
|
-
# "basic-auth": <bool>,
|
|
16
|
-
# "headers-data": <dict[str, str]>,
|
|
17
|
-
# "body-data": <dict[str, str],
|
|
18
|
-
# "token": <str>,
|
|
19
|
-
# "expiration": <timestamp>
|
|
20
|
-
# }
|
|
21
|
-
# }
|
|
22
|
-
_provider_registry: dict[str, dict[str, Any]] = {}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def provider_register(provider_id: str,
|
|
26
|
-
access_url: str,
|
|
27
|
-
auth_user: str,
|
|
28
|
-
auth_pwd: str,
|
|
29
|
-
custom_auth: tuple[str, str] = None,
|
|
30
|
-
headers_data: dict[str, str] = None,
|
|
31
|
-
body_data: dict[str, str] = None) -> None:
|
|
32
|
-
"""
|
|
33
|
-
Register an external authentication token provider.
|
|
34
|
-
|
|
35
|
-
If specified, *custom_auth* provides key names for sending credentials (username and password, in this order)
|
|
36
|
-
as key-value pairs in the body of the request. Otherwise, the external provider *provider_id* uses the standard
|
|
37
|
-
HTTP Basic Authorization scheme, wherein the credentials are B64-encoded and send in the request headers.
|
|
38
|
-
|
|
39
|
-
Optional constant key-value pairs (such as ['Content-Type', 'application/x-www-form-urlencoded']), to be
|
|
40
|
-
added to the request headers, may be specified in *headers_data*. Likewise, optional constant key-value pairs
|
|
41
|
-
(such as ['grant_type', 'client_credentials']), to be added to the request body, may be specified in *body_data*.
|
|
42
|
-
|
|
43
|
-
:param provider_id: the provider's identification
|
|
44
|
-
:param access_url: the url to request authentication tokens with
|
|
45
|
-
:param auth_user: the basic authorization user
|
|
46
|
-
:param auth_pwd: the basic authorization password
|
|
47
|
-
:param custom_auth: optional key names for sending the credentials as key-value pairs in the body of the request
|
|
48
|
-
:param headers_data: optional key-value pairs to be added to the request headers
|
|
49
|
-
:param body_data: optional key-value pairs to be added to the request body
|
|
50
|
-
"""
|
|
51
|
-
global _provider_registry # noqa: PLW0602
|
|
52
|
-
_provider_registry[provider_id] = {
|
|
53
|
-
"url": access_url,
|
|
54
|
-
"user": auth_user,
|
|
55
|
-
"pwd": auth_pwd,
|
|
56
|
-
"custom-auth": custom_auth,
|
|
57
|
-
"headers-data": headers_data,
|
|
58
|
-
"body-data": body_data,
|
|
59
|
-
"token": None,
|
|
60
|
-
"expiration": datetime.now(tz=TZ_LOCAL).timestamp()
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
def provider_get_token(provider_id: str,
|
|
65
|
-
errors: list[str] = None,
|
|
66
|
-
logger: Logger = None) -> str | None:
|
|
67
|
-
"""
|
|
68
|
-
Obtain an authentication token from the external provider *provider_id*.
|
|
69
|
-
|
|
70
|
-
:param provider_id: the provider's identification
|
|
71
|
-
:param errors: incidental error messages
|
|
72
|
-
:param logger: optional logger
|
|
73
|
-
"""
|
|
74
|
-
# initialize the return variable
|
|
75
|
-
result: str | None = None
|
|
76
|
-
|
|
77
|
-
global _provider_registry # noqa: PLW0602
|
|
78
|
-
err_msg: str | None = None
|
|
79
|
-
provider: dict[str, Any] = _provider_registry.get(provider_id)
|
|
80
|
-
if provider:
|
|
81
|
-
now: float = datetime.now(tz=TZ_LOCAL).timestamp()
|
|
82
|
-
if now > provider.get("expiration"):
|
|
83
|
-
user: str = provider.get("user")
|
|
84
|
-
pwd: str = provider.get("pwd")
|
|
85
|
-
headers_data: dict[str, str] = provider.get("headers-data") or {}
|
|
86
|
-
body_data: dict[str, str] = provider.get("body-data") or {}
|
|
87
|
-
custom_auth: tuple[str, str] = provider.get("custom-auth")
|
|
88
|
-
if custom_auth:
|
|
89
|
-
body_data[custom_auth[0]] = user
|
|
90
|
-
body_data[custom_auth[1]] = pwd
|
|
91
|
-
else:
|
|
92
|
-
enc_bytes: bytes = b64encode(f"{user}:{pwd}".encode())
|
|
93
|
-
headers_data["Authorization"] = f"Basic {enc_bytes.decode()}"
|
|
94
|
-
url: str = provider.get("url")
|
|
95
|
-
try:
|
|
96
|
-
# typical return on a token request:
|
|
97
|
-
# {
|
|
98
|
-
# "token_type": "Bearer",
|
|
99
|
-
# "access_token": <str>,
|
|
100
|
-
# "expires_in": <number-of-seconds>,
|
|
101
|
-
# optional data:
|
|
102
|
-
# "refresh_token": <str>,
|
|
103
|
-
# "refresh_expires_in": <number-of-seconds>
|
|
104
|
-
# }
|
|
105
|
-
response: requests.Response = requests.post(url=url,
|
|
106
|
-
data=body_data,
|
|
107
|
-
headers=headers_data,
|
|
108
|
-
timeout=None)
|
|
109
|
-
if response.status_code < 200 or response.status_code >= 300:
|
|
110
|
-
# request resulted in error, report the problem
|
|
111
|
-
err_msg = (f"POST '{url}': failed, "
|
|
112
|
-
f"status {response.status_code}, reason '{response.reason}'")
|
|
113
|
-
else:
|
|
114
|
-
reply: dict[str, Any] = response.json()
|
|
115
|
-
provider["token"] = reply.get("access_token")
|
|
116
|
-
provider["expiration"] = now + int(reply.get("expires_in"))
|
|
117
|
-
if logger:
|
|
118
|
-
logger.debug(msg=f"POST '{url}': status {response.status_code}")
|
|
119
|
-
except Exception as e:
|
|
120
|
-
# the operation raised an exception
|
|
121
|
-
err_msg = exc_format(exc=e,
|
|
122
|
-
exc_info=sys.exc_info())
|
|
123
|
-
err_msg = f"POST '{url}': error, '{err_msg}'"
|
|
124
|
-
else:
|
|
125
|
-
err_msg: str = f"Provider '{provider_id}' not registered"
|
|
126
|
-
|
|
127
|
-
if err_msg:
|
|
128
|
-
if isinstance(errors, list):
|
|
129
|
-
errors.append(err_msg)
|
|
130
|
-
if logger:
|
|
131
|
-
logger.error(msg=err_msg)
|
|
132
|
-
else:
|
|
133
|
-
result = provider.get("token")
|
|
134
|
-
|
|
135
|
-
return result
|
|
136
|
-
|
|
137
|
-
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
pypomes_jwt/__init__.py,sha256=vXAeaEnuUqpvGtV465TsW2Lf3ihijrMP2Hm4My79y88,968
|
|
2
|
-
pypomes_jwt/jwt_config.py,sha256=wi3NmUsSAvSXknU1AZd-7h406J4JeBBi9XwPw0VwsAc,4026
|
|
3
|
-
pypomes_jwt/jwt_pomes.py,sha256=WWmZYVpG6wqlIjcJU3jNyBquCgfB-OcgCJBmm6imL4Q,23524
|
|
4
|
-
pypomes_jwt/jwt_providers.py,sha256=iwTUUHJJuIdSJi9n3o2r3YP1nd0BFGL3BO8ET7Y4NNs,5858
|
|
5
|
-
pypomes_jwt/jwt_registry.py,sha256=ypBEoL0I2F08sR2G2VO9wXxVeE252lNzjIAC3FGORhA,22631
|
|
6
|
-
pypomes_jwt-1.3.7.dist-info/METADATA,sha256=hohXdlI9ilzv7dR6rWuRHLF1UAFx2OqbpHk6q5KYvW8,627
|
|
7
|
-
pypomes_jwt-1.3.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
8
|
-
pypomes_jwt-1.3.7.dist-info/licenses/LICENSE,sha256=NdakochSXm_H_-DSL_x2JlRCkYikj3snYYvTwgR5d_c,1086
|
|
9
|
-
pypomes_jwt-1.3.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|