pypomes-jwt 0.6.2__py3-none-any.whl → 0.6.3__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/jwt_data.py +89 -67
- pypomes_jwt/jwt_pomes.py +39 -21
- {pypomes_jwt-0.6.2.dist-info → pypomes_jwt-0.6.3.dist-info}/METADATA +1 -1
- pypomes_jwt-0.6.3.dist-info/RECORD +7 -0
- pypomes_jwt-0.6.2.dist-info/RECORD +0 -7
- {pypomes_jwt-0.6.2.dist-info → pypomes_jwt-0.6.3.dist-info}/WHEEL +0 -0
- {pypomes_jwt-0.6.2.dist-info → pypomes_jwt-0.6.3.dist-info}/licenses/LICENSE +0 -0
pypomes_jwt/jwt_data.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
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
6
|
from pypomes_core import str_random
|
|
@@ -18,6 +18,17 @@ class JwtData:
|
|
|
18
18
|
- access_data: list with dictionaries holding the JWT token data:
|
|
19
19
|
[
|
|
20
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
|
+
},
|
|
21
32
|
"reserved-claims": { # reserved claims
|
|
22
33
|
"exp": <timestamp>, # expiration time
|
|
23
34
|
"iat": <timestamp> # issued at
|
|
@@ -39,17 +50,6 @@ class JwtData:
|
|
|
39
50
|
"<custom-claim-key-1>": "<custom-claim-value-1>",
|
|
40
51
|
...
|
|
41
52
|
"<custom-claim-key-n>": "<custom-claim-value-n>"
|
|
42
|
-
},
|
|
43
|
-
"control-data": { # control data
|
|
44
|
-
"remote-provider": <bool>, # whether the JWT provider is a remote server
|
|
45
|
-
"access-token": <jwt-token>, # access token
|
|
46
|
-
"algorithm": <string>, # HS256, HS512, RSA256, RSA512
|
|
47
|
-
"request-timeout": <int>, # in seconds - defaults to no timeout
|
|
48
|
-
"access-max-age": <int>, # in seconds - defaults to JWT_ACCESS_MAX_AGE
|
|
49
|
-
"refresh-exp": <timestamp>, # expiration time for the refresh operation
|
|
50
|
-
"secret-key": <bytes>, # HS secret key
|
|
51
|
-
"private-key": <bytes>, # RSA private key
|
|
52
|
-
"public-key": <bytes>, # RSA public key
|
|
53
53
|
}
|
|
54
54
|
},
|
|
55
55
|
...
|
|
@@ -69,9 +69,9 @@ class JwtData:
|
|
|
69
69
|
algorithm: Literal["HS256", "HS512", "RSA256", "RSA512"],
|
|
70
70
|
access_max_age: int,
|
|
71
71
|
refresh_max_age: int,
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
hs_secret_key: bytes,
|
|
73
|
+
rsa_private_key: bytes,
|
|
74
|
+
rsa_public_key: bytes,
|
|
75
75
|
request_timeout: int,
|
|
76
76
|
remote_provider: bool,
|
|
77
77
|
logger: Logger = None) -> None:
|
|
@@ -79,11 +79,11 @@ class JwtData:
|
|
|
79
79
|
Add to storage the parameters needed to produce and validate JWT tokens for *account_id*.
|
|
80
80
|
|
|
81
81
|
The parameter *claims* may contain public and custom claims. Currently, the public claims supported
|
|
82
|
-
are *birthdate*, *email*, *gender*, *name*, and *roles*. Everything else
|
|
83
|
-
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.
|
|
84
84
|
|
|
85
85
|
Presently, the *refresh_max_age* data is not relevant, as the authorization parameters in *claims*
|
|
86
|
-
(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.
|
|
87
87
|
This situation might change in the future.
|
|
88
88
|
|
|
89
89
|
:param account_id: the account identification
|
|
@@ -92,28 +92,28 @@ class JwtData:
|
|
|
92
92
|
:param algorithm: the algorithm used to sign the token with
|
|
93
93
|
:param access_max_age: token duration (in seconds)
|
|
94
94
|
:param refresh_max_age: duration for the refresh operation (in seconds)
|
|
95
|
-
:param
|
|
96
|
-
:param
|
|
97
|
-
: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
|
|
98
98
|
:param request_timeout: timeout for the requests to the reference URL
|
|
99
99
|
:param remote_provider: whether the JWT provider is a remote server
|
|
100
100
|
:param logger: optional logger
|
|
101
101
|
"""
|
|
102
102
|
# Do the access data already exist ?
|
|
103
|
-
if not self.
|
|
103
|
+
if not self.get_access_data(account_id=account_id):
|
|
104
104
|
# no, build control data
|
|
105
105
|
control_data: dict[str, Any] = {
|
|
106
106
|
"algorithm": algorithm,
|
|
107
107
|
"access-max-age": access_max_age,
|
|
108
108
|
"request-timeout": request_timeout,
|
|
109
109
|
"remote-provider": remote_provider,
|
|
110
|
-
"refresh-exp": datetime.now(tz=timezone.utc) +
|
|
110
|
+
"refresh-exp": datetime.now(tz=timezone.utc).timestamp() + refresh_max_age
|
|
111
111
|
}
|
|
112
112
|
if algorithm in ["HS256", "HS512"]:
|
|
113
|
-
control_data["secret-key"] =
|
|
113
|
+
control_data["hs-secret-key"] = hs_secret_key
|
|
114
114
|
else:
|
|
115
|
-
control_data["private-key"] =
|
|
116
|
-
control_data["public-key"] =
|
|
115
|
+
control_data["rsa-private-key"] = rsa_private_key
|
|
116
|
+
control_data["rsa-public-key"] = rsa_public_key
|
|
117
117
|
|
|
118
118
|
# build claims
|
|
119
119
|
reserved_claims: dict[str, Any] = {
|
|
@@ -154,8 +154,8 @@ class JwtData:
|
|
|
154
154
|
:param logger: optional logger
|
|
155
155
|
"""
|
|
156
156
|
# obtain the access data item in storage
|
|
157
|
-
item_data: dict[str, dict[str, Any]] = self.
|
|
158
|
-
|
|
157
|
+
item_data: dict[str, dict[str, Any]] = self.get_access_data(account_id=account_id,
|
|
158
|
+
logger=logger)
|
|
159
159
|
if item_data:
|
|
160
160
|
with self.access_lock:
|
|
161
161
|
self.access_data.remove(item_data)
|
|
@@ -173,6 +173,7 @@ class JwtData:
|
|
|
173
173
|
Structure of the return data:
|
|
174
174
|
{
|
|
175
175
|
"access_token": <jwt-token>,
|
|
176
|
+
"created_in": <timestamp>,
|
|
176
177
|
"expires_in": <seconds-to-expiration>
|
|
177
178
|
}
|
|
178
179
|
|
|
@@ -196,8 +197,8 @@ class JwtData:
|
|
|
196
197
|
result: dict[str, Any]
|
|
197
198
|
|
|
198
199
|
# obtain the item in storage
|
|
199
|
-
item_data: dict[str, Any] = self.
|
|
200
|
-
|
|
200
|
+
item_data: dict[str, Any] = self.get_access_data(account_id=account_id,
|
|
201
|
+
logger=logger)
|
|
201
202
|
# was the JWT data obtained ?
|
|
202
203
|
if item_data:
|
|
203
204
|
# yes, proceed
|
|
@@ -208,41 +209,51 @@ class JwtData:
|
|
|
208
209
|
|
|
209
210
|
# obtain a new token, if the current token has expired
|
|
210
211
|
if just_now > reserved_claims.get("exp"):
|
|
211
|
-
# where is the
|
|
212
|
+
# where is the JWT service provider ?
|
|
212
213
|
if control_data.get("remote-provider"):
|
|
213
214
|
# JWT service is being provided by a remote server
|
|
214
215
|
errors: list[str] = []
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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:
|
|
221
229
|
with self.access_lock:
|
|
222
|
-
control_data["access-token"] =
|
|
223
|
-
|
|
224
|
-
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")
|
|
225
234
|
else:
|
|
226
235
|
raise RuntimeError(" - ".join(errors))
|
|
227
236
|
else:
|
|
228
237
|
# JWT service is being provided locally
|
|
229
|
-
reserved_claims["jti"] = str_random(size=16)
|
|
230
|
-
reserved_claims["iat"] = just_now
|
|
231
|
-
reserved_claims["exp"] = just_now + control_data.get("access-max-age")
|
|
232
238
|
claims: dict[str, Any] = item_data.get("public-claims").copy()
|
|
233
239
|
claims.update(m=reserved_claims)
|
|
234
240
|
claims.update(m=custom_claims)
|
|
235
241
|
# may raise an exception
|
|
236
242
|
token: str = jwt.encode(payload=claims,
|
|
237
|
-
key=control_data.get("secret-key") or
|
|
243
|
+
key=(control_data.get("hs-secret-key") or
|
|
244
|
+
control_data.get("rsa-private-key")),
|
|
238
245
|
algorithm=control_data.get("algorithm"))
|
|
239
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")
|
|
240
250
|
control_data["access-token"] = token
|
|
241
251
|
|
|
242
252
|
# return the token
|
|
243
253
|
result = {
|
|
244
254
|
"access_token": control_data.get("access-token"),
|
|
245
|
-
"
|
|
255
|
+
"created_in": reserved_claims.get("iat"),
|
|
256
|
+
"expires_in": reserved_claims.get("exp") - reserved_claims.get("iat")
|
|
246
257
|
}
|
|
247
258
|
else:
|
|
248
259
|
# JWT access data not found
|
|
@@ -265,37 +276,46 @@ class JwtData:
|
|
|
265
276
|
:raises InvalidTokenError: token is not valid
|
|
266
277
|
:raises ExpiredSignatureError: token has expired
|
|
267
278
|
"""
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
with self.access_lock:
|
|
271
|
-
for item_data in self.access_data:
|
|
272
|
-
control_data: dict[str, Any] = item_data.get("control-data")
|
|
273
|
-
if token == control_data.get("access-token"):
|
|
274
|
-
algorithm = control_data.get("algorithm")
|
|
275
|
-
key = control_data.get("public-key") or control_data.get("secret-key")
|
|
276
|
-
break
|
|
277
|
-
|
|
278
|
-
if not algorithm or not key:
|
|
279
|
-
raise InvalidTokenError("JWT token is not valid")
|
|
279
|
+
# declare the return variable
|
|
280
|
+
result: dict[str, Any]
|
|
280
281
|
|
|
281
282
|
if logger:
|
|
282
283
|
logger.debug(msg=f"Retrieve claims for JWT token '{token}'")
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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
|
+
|
|
286
300
|
if logger:
|
|
287
301
|
logger.debug(f"Retrieved claims for JWT token '{token}': {result}")
|
|
288
302
|
|
|
289
303
|
return result
|
|
290
304
|
|
|
291
|
-
def
|
|
292
|
-
|
|
293
|
-
|
|
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]]:
|
|
294
309
|
# noinspection HttpUrlsUsage
|
|
295
310
|
"""
|
|
296
|
-
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.
|
|
297
316
|
|
|
298
317
|
:param account_id: the account identification
|
|
318
|
+
:param access_token: the access token
|
|
299
319
|
:param logger: optional logger
|
|
300
320
|
:return: the corresponding item in storage, or *None* if not found
|
|
301
321
|
"""
|
|
@@ -303,11 +323,13 @@ class JwtData:
|
|
|
303
323
|
result: dict[str, dict[str, Any]] | None = None
|
|
304
324
|
|
|
305
325
|
if logger:
|
|
306
|
-
|
|
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}")
|
|
307
328
|
# retrieve the data
|
|
308
329
|
with self.access_lock:
|
|
309
330
|
for item_data in self.access_data:
|
|
310
|
-
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")):
|
|
311
333
|
result = item_data
|
|
312
334
|
break
|
|
313
335
|
if logger:
|
|
@@ -322,7 +344,7 @@ def jwt_request_token(errors: list[str],
|
|
|
322
344
|
timeout: int = None,
|
|
323
345
|
logger: Logger = None) -> dict[str, Any]:
|
|
324
346
|
"""
|
|
325
|
-
Obtain and return the JWT token
|
|
347
|
+
Obtain and return the JWT token from *reference_url*, along with its duration.
|
|
326
348
|
|
|
327
349
|
Expected structure of the return data:
|
|
328
350
|
{
|
pypomes_jwt/jwt_pomes.py
CHANGED
|
@@ -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
|
|
@@ -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
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
pypomes_jwt/__init__.py,sha256=m0USOMlGVUfofwukykKf6DAPq7CRn4SiY6CeNOOiqJ8,998
|
|
2
|
+
pypomes_jwt/jwt_data.py,sha256=5GB5NgmVeTJinlfIAO7BaWO0aPCETqG3dxm-aP99pCk,19222
|
|
3
|
+
pypomes_jwt/jwt_pomes.py,sha256=U8Vc0IOlW5-XmRR_Px2xLlVit5oAHVnfBcNwDzwh_8I,14786
|
|
4
|
+
pypomes_jwt-0.6.3.dist-info/METADATA,sha256=WaVMfTzEO-cnKmP7XDm-p8owJxmIhzua6yk61s0l86E,599
|
|
5
|
+
pypomes_jwt-0.6.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
6
|
+
pypomes_jwt-0.6.3.dist-info/licenses/LICENSE,sha256=NdakochSXm_H_-DSL_x2JlRCkYikj3snYYvTwgR5d_c,1086
|
|
7
|
+
pypomes_jwt-0.6.3.dist-info/RECORD,,
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
pypomes_jwt/__init__.py,sha256=m0USOMlGVUfofwukykKf6DAPq7CRn4SiY6CeNOOiqJ8,998
|
|
2
|
-
pypomes_jwt/jwt_data.py,sha256=auVG-sQJjeeQwAzwkaSV149_qHwLIYqoi-Aa0op9eI8,17830
|
|
3
|
-
pypomes_jwt/jwt_pomes.py,sha256=93o0QC7Phsb_29KaLn9mlfE6nUw8HXadqpCZn-Q8gvI,13891
|
|
4
|
-
pypomes_jwt-0.6.2.dist-info/METADATA,sha256=2rj4pjbMCHX_lOmN59UAMGscCesvMvYQgkzvcSCc50s,599
|
|
5
|
-
pypomes_jwt-0.6.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
6
|
-
pypomes_jwt-0.6.2.dist-info/licenses/LICENSE,sha256=NdakochSXm_H_-DSL_x2JlRCkYikj3snYYvTwgR5d_c,1086
|
|
7
|
-
pypomes_jwt-0.6.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|