jamlib 3.2.0b0__tar.gz → 3.2.0b2__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.
- {jamlib-3.2.0b0/src/jamlib.egg-info → jamlib-3.2.0b2}/PKG-INFO +1 -1
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/pyproject.toml +1 -1
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/__init__.py +1 -1
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/aio/__base__.py +8 -1
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/aio/instance.py +37 -7
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/exceptions/__init__.py +2 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/exceptions/jwt.py +5 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/ext/flask/extensions.py +3 -2
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/ext/litestar/middleware.py +4 -3
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/ext/litestar/plugins.py +1 -1
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/ext/starlette/backends.py +3 -2
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/instance.py +16 -9
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jose/__base__.py +3 -13
- jamlib-3.2.0b2/src/jam/jose/__init__.py +55 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jose/jwe.py +2 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jose/jwk.py +2 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jose/jwt.py +165 -119
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jwt/__init__.py +1 -1
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/tests/clients.py +1 -1
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/tests/fakers.py +1 -1
- {jamlib-3.2.0b0 → jamlib-3.2.0b2/src/jamlib.egg-info}/PKG-INFO +1 -1
- jamlib-3.2.0b0/src/jam/jose/__init__.py +0 -19
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/LICENSE.md +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/README.md +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/setup.cfg +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/__base__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/__base_encoder__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/__deprecated__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/aio/__init__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/aio/jwt/__init__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/aio/oauth2/__base__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/aio/oauth2/__init__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/aio/oauth2/builtin/__init__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/aio/oauth2/builtin/github.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/aio/oauth2/builtin/gitlab.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/aio/oauth2/builtin/google.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/aio/oauth2/builtin/yandex.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/aio/oauth2/client.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/aio/sessions/__base__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/aio/sessions/__init__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/aio/sessions/json.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/aio/sessions/redis.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/cli/__init__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/cli/cli.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/cli/commands/__init__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/cli/commands/keys.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/cli/commands/password.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/encoders.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/exceptions/base.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/exceptions/jose.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/exceptions/oauth2.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/exceptions/paseto.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/exceptions/plugins.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/exceptions/sessions.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/ext/__init__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/ext/fastapi/__init__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/ext/flask/__init__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/ext/flask/objects.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/ext/litestar/__init__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/ext/litestar/objects.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/ext/starlette/__init__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/ext/starlette/objects.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jose/__algorithms__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jose/jws.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jose/lists/__base__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jose/lists/__init__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jose/lists/json.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jose/lists/memory.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jose/lists/redis.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jose/utils.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jwt/__algorithms__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jwt/__base__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jwt/__types__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jwt/lists/__base__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jwt/lists/__init__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jwt/lists/json.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jwt/lists/redis.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jwt/module.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jwt/utils.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/logger.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/oauth2/__base__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/oauth2/__init__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/oauth2/builtin/__init__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/oauth2/builtin/github.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/oauth2/builtin/gitlab.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/oauth2/builtin/google.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/oauth2/builtin/yandex.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/oauth2/client.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/otp/__base__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/otp/__init__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/otp/hotp.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/otp/totp.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/paseto/__base__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/paseto/__init__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/paseto/utils.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/paseto/v1.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/paseto/v2.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/paseto/v3.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/paseto/v4.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/py.typed +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/sessions/__base__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/sessions/__init__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/sessions/json.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/sessions/redis.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/tests/__init__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/utils/__init__.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/utils/aes.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/utils/await_maybe.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/utils/basic_auth.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/utils/config_maker.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/utils/ed.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/utils/otp_keys.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/utils/rsa.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/utils/salt_hash.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/utils/symmetric.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/utils/xchacha20poly1305.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/utils/xor.py +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jamlib.egg-info/SOURCES.txt +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jamlib.egg-info/dependency_links.txt +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jamlib.egg-info/entry_points.txt +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jamlib.egg-info/requires.txt +0 -0
- {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jamlib.egg-info/top_level.txt +0 -0
|
@@ -75,7 +75,12 @@ class BaseAsyncJam(BaseJam):
|
|
|
75
75
|
|
|
76
76
|
@abstractmethod
|
|
77
77
|
async def jwt_decode( # type: ignore[override]
|
|
78
|
-
self,
|
|
78
|
+
self,
|
|
79
|
+
token: str,
|
|
80
|
+
check_exp: bool = True,
|
|
81
|
+
check_list: bool = True,
|
|
82
|
+
check_nbf: bool = False,
|
|
83
|
+
include_headers: bool = False,
|
|
79
84
|
) -> dict[str, Any]:
|
|
80
85
|
"""Verify and decode JWT token.
|
|
81
86
|
|
|
@@ -83,6 +88,8 @@ class BaseAsyncJam(BaseJam):
|
|
|
83
88
|
token (str): JWT token
|
|
84
89
|
check_exp (bool): Check expire
|
|
85
90
|
check_list (bool): Check white/black list. Docs: https://jam.makridenko.ru/jwt/lists/what/
|
|
91
|
+
check_nbf (bool): Check not-before time
|
|
92
|
+
include_headers (bool): Include headers in the decoded payload
|
|
86
93
|
|
|
87
94
|
Returns:
|
|
88
95
|
dict[str, Any]: Decoded payload
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
|
|
3
3
|
import datetime
|
|
4
|
+
import time
|
|
4
5
|
from typing import Any
|
|
5
6
|
import uuid
|
|
6
7
|
|
|
@@ -10,6 +11,7 @@ from jam.exceptions import (
|
|
|
10
11
|
JamJWTExpired,
|
|
11
12
|
JamJWTInBlackList,
|
|
12
13
|
JamJWTNotInWhiteList,
|
|
14
|
+
JamJWTNotYetValid,
|
|
13
15
|
)
|
|
14
16
|
|
|
15
17
|
|
|
@@ -108,7 +110,12 @@ class Jam(BaseAsyncJam):
|
|
|
108
110
|
return token
|
|
109
111
|
|
|
110
112
|
async def jwt_decode(
|
|
111
|
-
self,
|
|
113
|
+
self,
|
|
114
|
+
token: str,
|
|
115
|
+
check_exp: bool = True,
|
|
116
|
+
check_list: bool = True,
|
|
117
|
+
check_nbf: bool = False,
|
|
118
|
+
include_headers: bool = False,
|
|
112
119
|
) -> dict[str, Any]:
|
|
113
120
|
"""Verify and decode JWT token.
|
|
114
121
|
|
|
@@ -116,23 +123,43 @@ class Jam(BaseAsyncJam):
|
|
|
116
123
|
token (str): JWT token
|
|
117
124
|
check_exp (bool): Check expire
|
|
118
125
|
check_list (bool): Check white/black list. Docs: https://jam.makridenko.ru/jwt/lists/what/
|
|
126
|
+
check_nbf (bool): Check not-before time
|
|
127
|
+
include_headers (bool): Include headers in the decoded payload
|
|
119
128
|
|
|
120
129
|
Returns:
|
|
121
130
|
dict[str, Any]: Decoded payload
|
|
131
|
+
|
|
132
|
+
Raises:
|
|
133
|
+
JamJWTExpired: If token is expired
|
|
134
|
+
JamJWTNotYetValid: If token is not yet valid (nbf claim)
|
|
135
|
+
JamConfigurationError: If JWT list is not connected
|
|
136
|
+
JamJWTNotInWhiteList: If token is not in white list
|
|
137
|
+
JamJWTInBlackList: If token is in black list
|
|
122
138
|
"""
|
|
123
139
|
assert self.jwt is not None
|
|
124
140
|
self._logger.debug(
|
|
125
|
-
f"Verifying JWT token (length: {len(token)} chars), check_exp={check_exp}, check_list={check_list}"
|
|
141
|
+
f"Verifying JWT token (length: {len(token)} chars), check_exp={check_exp}, check_list={check_list}, check_nbf={check_nbf}"
|
|
126
142
|
)
|
|
127
|
-
|
|
143
|
+
data = self.jwt.decode(token)
|
|
144
|
+
if "payload" in data:
|
|
145
|
+
payload = data["payload"]
|
|
146
|
+
headers = data.get("header")
|
|
147
|
+
else:
|
|
148
|
+
payload = data
|
|
149
|
+
headers = None
|
|
150
|
+
|
|
151
|
+
if check_exp and "exp" in payload:
|
|
152
|
+
if payload["exp"] < time.time():
|
|
153
|
+
raise JamJWTExpired
|
|
154
|
+
|
|
155
|
+
if check_nbf and "nbf" in payload:
|
|
156
|
+
if payload["nbf"] > time.time():
|
|
157
|
+
raise JamJWTNotYetValid
|
|
158
|
+
|
|
128
159
|
self._logger.debug(
|
|
129
160
|
f"JWT token verified successfully, payload keys: {list(payload.keys())}"
|
|
130
161
|
)
|
|
131
162
|
|
|
132
|
-
if check_exp:
|
|
133
|
-
if payload["exp"] < datetime.datetime.now().timestamp():
|
|
134
|
-
raise JamJWTExpired
|
|
135
|
-
|
|
136
163
|
if check_list:
|
|
137
164
|
if not self.jwt.list:
|
|
138
165
|
raise JamConfigurationError(
|
|
@@ -152,6 +179,9 @@ class Jam(BaseAsyncJam):
|
|
|
152
179
|
message="Invalid JWT list type",
|
|
153
180
|
error_code="configuration.jwt.unknown_list_type",
|
|
154
181
|
)
|
|
182
|
+
|
|
183
|
+
if include_headers and headers is not None:
|
|
184
|
+
return {"header": headers, "payload": payload}
|
|
155
185
|
return payload
|
|
156
186
|
|
|
157
187
|
async def session_create(
|
|
@@ -15,6 +15,7 @@ from .jwt import (
|
|
|
15
15
|
JamJWTExpired,
|
|
16
16
|
JamJWTInBlackList,
|
|
17
17
|
JamJWTNotInWhiteList,
|
|
18
|
+
JamJWTNotYetValid,
|
|
18
19
|
JamJWTUnsupportedAlgorithm,
|
|
19
20
|
JamJWTValidationError,
|
|
20
21
|
)
|
|
@@ -56,6 +57,7 @@ __all__ = [
|
|
|
56
57
|
"JamJWTExpired",
|
|
57
58
|
"JamJWTInBlackList",
|
|
58
59
|
"JamJWTNotInWhiteList",
|
|
60
|
+
"JamJWTNotYetValid",
|
|
59
61
|
"JamJWTEmptyPrivateKey",
|
|
60
62
|
"JamJWTEmptySecretKey",
|
|
61
63
|
"JamJWTUnsupportedAlgorithm",
|
|
@@ -8,6 +8,11 @@ class JamJWTExpired(JamError):
|
|
|
8
8
|
default_code = "jwt.token_expired"
|
|
9
9
|
|
|
10
10
|
|
|
11
|
+
class JamJWTNotYetValid(JamError):
|
|
12
|
+
default_message = "Token is not yet valid (nbf claim)."
|
|
13
|
+
default_code = "jwt.token_not_yet_valid"
|
|
14
|
+
|
|
15
|
+
|
|
11
16
|
class JamJWTInBlackList(JamError):
|
|
12
17
|
default_message = "Token in blacklist."
|
|
13
18
|
default_code = "jwt.blacklist"
|
|
@@ -5,7 +5,7 @@ from typing import Any
|
|
|
5
5
|
import flask
|
|
6
6
|
|
|
7
7
|
from jam.exceptions import JamFlaskPluginConfigError
|
|
8
|
-
from jam.
|
|
8
|
+
from jam.jose import create_instance as create_jwt
|
|
9
9
|
from jam.oauth2 import create_instance as create_oauth2
|
|
10
10
|
from jam.paseto import create_instance as create_paseto
|
|
11
11
|
from jam.sessions import create_instance as create_session
|
|
@@ -170,7 +170,8 @@ class JWTExtension(BaseAuthExtension):
|
|
|
170
170
|
if not token:
|
|
171
171
|
return None
|
|
172
172
|
try:
|
|
173
|
-
|
|
173
|
+
result = self._auth.decode(token)
|
|
174
|
+
payload = result.get("payload", result)
|
|
174
175
|
flask.g.payload = payload
|
|
175
176
|
return payload
|
|
176
177
|
except Exception:
|
|
@@ -11,7 +11,7 @@ from litestar.middleware import (
|
|
|
11
11
|
from jam.aio.sessions.__base__ import BaseAsyncSessionModule
|
|
12
12
|
from jam.exceptions import JamLitestarPluginConfigError, JamLitestarPluginError
|
|
13
13
|
from jam.ext.litestar.objects import BaseUser, Token
|
|
14
|
-
from jam.
|
|
14
|
+
from jam.jose.__base__ import BaseJWT as JWTClass
|
|
15
15
|
from jam.paseto import BasePASETO
|
|
16
16
|
|
|
17
17
|
|
|
@@ -46,7 +46,7 @@ class BaseMiddleware(AbstractAuthenticationMiddleware):
|
|
|
46
46
|
class JWTMiddleware(BaseMiddleware):
|
|
47
47
|
"""JWT Middleware for litestar."""
|
|
48
48
|
|
|
49
|
-
AUTH_MODULE:
|
|
49
|
+
AUTH_MODULE: JWTClass
|
|
50
50
|
LIST: bool = False
|
|
51
51
|
|
|
52
52
|
async def authenticate_request( # noqa
|
|
@@ -57,7 +57,8 @@ class JWTMiddleware(BaseMiddleware):
|
|
|
57
57
|
if not token:
|
|
58
58
|
return AuthenticationResult(user=None, auth=token_model)
|
|
59
59
|
try:
|
|
60
|
-
|
|
60
|
+
result = self.AUTH_MODULE.decode(token)
|
|
61
|
+
data = result.get("payload", result)
|
|
61
62
|
if self.AUTH_MODULE.list and self.LIST:
|
|
62
63
|
match self.AUTH_MODULE.list.__list_type__:
|
|
63
64
|
case "white":
|
|
@@ -16,7 +16,7 @@ from jam.ext.litestar.middleware import (
|
|
|
16
16
|
SessionMiddleware,
|
|
17
17
|
)
|
|
18
18
|
from jam.ext.litestar.objects import BaseUser
|
|
19
|
-
from jam.
|
|
19
|
+
from jam.jose import create_instance as create_jwt
|
|
20
20
|
from jam.oauth2 import create_instance as create_oauth2
|
|
21
21
|
from jam.paseto import create_instance as create_paseto
|
|
22
22
|
from jam.utils.config_maker import GENERIC_POINTER, __config_maker__
|
|
@@ -14,7 +14,7 @@ from starlette.requests import HTTPConnection
|
|
|
14
14
|
from jam.aio.sessions import create_instance as create_session
|
|
15
15
|
from jam.exceptions import JamStarlettePluginConfigError
|
|
16
16
|
from jam.ext.starlette.objects import BaseUser, SimpleUser
|
|
17
|
-
from jam.
|
|
17
|
+
from jam.jose import create_instance as create_jwt
|
|
18
18
|
from jam.paseto import create_instance as create_paseto
|
|
19
19
|
from jam.utils.config_maker import GENERIC_POINTER, __config_maker__
|
|
20
20
|
|
|
@@ -141,7 +141,8 @@ class JWTBackend(BaseBackend):
|
|
|
141
141
|
if self._auth.list.check(token):
|
|
142
142
|
return AuthCredentials(None), UnauthenticatedUser()
|
|
143
143
|
data = self._auth.decode(token)
|
|
144
|
-
|
|
144
|
+
payload = data.get("payload", data)
|
|
145
|
+
user = self._user.from_payload(payload)
|
|
145
146
|
return AuthCredentials(["authenticated"]), user
|
|
146
147
|
|
|
147
148
|
return AuthCredentials(None), UnauthenticatedUser()
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
|
|
3
3
|
import datetime
|
|
4
|
+
import time
|
|
4
5
|
from typing import Any
|
|
5
6
|
import uuid
|
|
6
7
|
|
|
@@ -8,8 +9,10 @@ from jam.__base__ import BaseJam
|
|
|
8
9
|
from jam.__deprecated__ import deprecated
|
|
9
10
|
from jam.exceptions import (
|
|
10
11
|
JamConfigurationError,
|
|
12
|
+
JamJWTExpired,
|
|
11
13
|
JamJWTInBlackList,
|
|
12
14
|
JamJWTNotInWhiteList,
|
|
15
|
+
JamJWTNotYetValid,
|
|
13
16
|
)
|
|
14
17
|
|
|
15
18
|
|
|
@@ -139,24 +142,25 @@ class Jam(BaseJam):
|
|
|
139
142
|
|
|
140
143
|
Raises:
|
|
141
144
|
JamJWTExpired: If token is expired
|
|
145
|
+
JamJWTNotYetValid: If token is not yet valid (nbf claim)
|
|
142
146
|
JamConfigurationError: If JWT list is not connected
|
|
143
147
|
JamJWTNotInWhiteList: If token is not in white list
|
|
144
148
|
JamJWTInBlackList: If token is in black list
|
|
145
149
|
"""
|
|
146
150
|
self._logger.debug(
|
|
147
|
-
f"Verifying JWT token (length: {len(token)} chars), check_exp={check_exp}, check_list={check_list}"
|
|
151
|
+
f"Verifying JWT token (length: {len(token)} chars), check_exp={check_exp}, check_list={check_list}, check_nbf={check_nbf}"
|
|
148
152
|
)
|
|
149
153
|
assert self.jwt is not None
|
|
150
|
-
data = self.jwt.decode(token
|
|
154
|
+
data = self.jwt.decode(token)
|
|
155
|
+
payload = data["payload"]
|
|
151
156
|
|
|
152
|
-
if
|
|
153
|
-
payload
|
|
154
|
-
|
|
155
|
-
payload = data
|
|
156
|
-
if isinstance(payload, bytes):
|
|
157
|
-
import json
|
|
157
|
+
if check_exp and "exp" in payload:
|
|
158
|
+
if payload["exp"] < time.time():
|
|
159
|
+
raise JamJWTExpired
|
|
158
160
|
|
|
159
|
-
|
|
161
|
+
if check_nbf and "nbf" in payload:
|
|
162
|
+
if payload["nbf"] > time.time():
|
|
163
|
+
raise JamJWTNotYetValid
|
|
160
164
|
|
|
161
165
|
self._logger.debug(
|
|
162
166
|
f"JWT token verified successfully, payload keys: {list(payload.keys())}"
|
|
@@ -181,6 +185,9 @@ class Jam(BaseJam):
|
|
|
181
185
|
message="Invalid JWT list type",
|
|
182
186
|
error_code="configuration.jwt.unknown_list_type",
|
|
183
187
|
)
|
|
188
|
+
|
|
189
|
+
if include_headers:
|
|
190
|
+
return data
|
|
184
191
|
return payload
|
|
185
192
|
|
|
186
193
|
def session_create(self, session_key: str, data: dict[str, Any]) -> str:
|
|
@@ -65,21 +65,11 @@ class BaseJWT(ABC):
|
|
|
65
65
|
raise NotImplementedError
|
|
66
66
|
|
|
67
67
|
@abstractmethod
|
|
68
|
-
def decode(
|
|
69
|
-
|
|
70
|
-
token: str,
|
|
71
|
-
include_headers: bool = False,
|
|
72
|
-
) -> dict[str, Any]:
|
|
73
|
-
"""Decode the JWT and return the payload.
|
|
74
|
-
|
|
75
|
-
Args:
|
|
76
|
-
token (str): The JWT to decode.
|
|
77
|
-
exp (bool): Whether to check the expiration time. Defaults to False.
|
|
78
|
-
nbf (bool): Whether to check the not-before time. Defaults to False.
|
|
79
|
-
include_headers (bool): Whether to include the headers in the result. Defaults to False.
|
|
68
|
+
def decode(self, token: str) -> dict[str, Any]:
|
|
69
|
+
"""Decode the JWT and return the header and payload.
|
|
80
70
|
|
|
81
71
|
Returns:
|
|
82
|
-
dict
|
|
72
|
+
dict with 'header' and 'payload' keys (both dicts).
|
|
83
73
|
"""
|
|
84
74
|
raise NotImplementedError
|
|
85
75
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
"""JOSE tools."""
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from jam.jose.jwk import JWK, JWKOct, JWKSet, JWKRSA, JWKEC
|
|
8
|
+
from jam.jose.jws import JWS
|
|
9
|
+
from jam.jose.jwe import JWE
|
|
10
|
+
from jam.jose.jwt import JWT
|
|
11
|
+
from jam.logger import BaseLogger, logger
|
|
12
|
+
from jam.encoders import BaseEncoder, JsonEncoder
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def create_instance(
|
|
16
|
+
alg: str,
|
|
17
|
+
secret: Any = None,
|
|
18
|
+
secret_key: Any = None,
|
|
19
|
+
password: str | bytes | None = None,
|
|
20
|
+
list: dict[str, Any] | None = None,
|
|
21
|
+
logger: BaseLogger = logger,
|
|
22
|
+
serializer: BaseEncoder | type[BaseEncoder] = JsonEncoder,
|
|
23
|
+
**kwargs: Any,
|
|
24
|
+
) -> JWT:
|
|
25
|
+
"""Create JWT instance."""
|
|
26
|
+
if secret is None and secret_key is not None:
|
|
27
|
+
secret = secret_key
|
|
28
|
+
elif secret is None:
|
|
29
|
+
raise ValueError("Either 'secret' or 'secret_key' must be provided")
|
|
30
|
+
|
|
31
|
+
key = secret
|
|
32
|
+
if isinstance(secret, JWK):
|
|
33
|
+
key = secret._to_keylike()
|
|
34
|
+
|
|
35
|
+
return JWT(
|
|
36
|
+
alg=alg,
|
|
37
|
+
secret_key=key,
|
|
38
|
+
password=password,
|
|
39
|
+
list=list,
|
|
40
|
+
serializer=serializer,
|
|
41
|
+
logger=logger,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
__all__ = [
|
|
46
|
+
"JWK",
|
|
47
|
+
"JWKSet",
|
|
48
|
+
"JWS",
|
|
49
|
+
"JWE",
|
|
50
|
+
"JWT",
|
|
51
|
+
"JWKRSA",
|
|
52
|
+
"JWKEC",
|
|
53
|
+
"JWKOct",
|
|
54
|
+
"create_instance",
|
|
55
|
+
]
|
|
@@ -31,10 +31,12 @@ class JWE(BaseJWE):
|
|
|
31
31
|
Provides encryption and decryption of data using JWK keys.
|
|
32
32
|
|
|
33
33
|
Example:
|
|
34
|
+
```python
|
|
34
35
|
>>> jwk = JWK.from_dict({"kty": "oct", "k": "your-secret-key"})
|
|
35
36
|
>>> jwe = JWE(alg="A128KW", enc="A128CBC-HS256", key=jwk)
|
|
36
37
|
>>> token = jwe.encrypt("secret data")
|
|
37
38
|
>>> plaintext = jwe.decrypt(token)
|
|
39
|
+
```
|
|
38
40
|
"""
|
|
39
41
|
|
|
40
42
|
def __init__(
|
|
@@ -142,9 +142,11 @@ class JWK(BaseJWK):
|
|
|
142
142
|
Provides JWK validation and signing capabilities.
|
|
143
143
|
|
|
144
144
|
Example:
|
|
145
|
+
```python
|
|
145
146
|
>>> jwk = JWK.from_dict({"kty": "oct", "k": "your-secret-key"})
|
|
146
147
|
>>> jwk.sign(b"data", "HS256")
|
|
147
148
|
>>> jwk.verify(token)
|
|
149
|
+
```
|
|
148
150
|
"""
|
|
149
151
|
|
|
150
152
|
_SUPPORTED_KEY_TYPES = frozenset({"RSA", "EC", "oct"})
|