jamlib 3.2.0b1__tar.gz → 3.2.0b3__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.
Files changed (122) hide show
  1. {jamlib-3.2.0b1/src/jamlib.egg-info → jamlib-3.2.0b3}/PKG-INFO +1 -1
  2. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/pyproject.toml +1 -1
  3. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/__base__.py +133 -21
  4. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/__init__.py +1 -1
  5. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/aio/__base__.py +69 -1
  6. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/aio/instance.py +76 -2
  7. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/ext/flask/extensions.py +3 -2
  8. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/ext/litestar/middleware.py +4 -3
  9. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/ext/litestar/plugins.py +1 -1
  10. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/ext/starlette/backends.py +3 -2
  11. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/instance.py +79 -2
  12. jamlib-3.2.0b3/src/jam/jose/__init__.py +113 -0
  13. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/jose/jwt.py +1 -1
  14. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/jwt/__init__.py +1 -1
  15. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/tests/clients.py +1 -1
  16. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/tests/fakers.py +1 -1
  17. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/utils/config_maker.py +35 -4
  18. {jamlib-3.2.0b1 → jamlib-3.2.0b3/src/jamlib.egg-info}/PKG-INFO +1 -1
  19. jamlib-3.2.0b1/src/jam/jose/__init__.py +0 -19
  20. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/LICENSE.md +0 -0
  21. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/README.md +0 -0
  22. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/setup.cfg +0 -0
  23. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/__base_encoder__.py +0 -0
  24. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/__deprecated__.py +0 -0
  25. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/aio/__init__.py +0 -0
  26. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/aio/jwt/__init__.py +0 -0
  27. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/aio/oauth2/__base__.py +0 -0
  28. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/aio/oauth2/__init__.py +0 -0
  29. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/aio/oauth2/builtin/__init__.py +0 -0
  30. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/aio/oauth2/builtin/github.py +0 -0
  31. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/aio/oauth2/builtin/gitlab.py +0 -0
  32. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/aio/oauth2/builtin/google.py +0 -0
  33. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/aio/oauth2/builtin/yandex.py +0 -0
  34. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/aio/oauth2/client.py +0 -0
  35. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/aio/sessions/__base__.py +0 -0
  36. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/aio/sessions/__init__.py +0 -0
  37. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/aio/sessions/json.py +0 -0
  38. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/aio/sessions/redis.py +0 -0
  39. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/cli/__init__.py +0 -0
  40. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/cli/cli.py +0 -0
  41. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/cli/commands/__init__.py +0 -0
  42. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/cli/commands/keys.py +0 -0
  43. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/cli/commands/password.py +0 -0
  44. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/encoders.py +0 -0
  45. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/exceptions/__init__.py +0 -0
  46. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/exceptions/base.py +0 -0
  47. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/exceptions/jose.py +0 -0
  48. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/exceptions/jwt.py +0 -0
  49. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/exceptions/oauth2.py +0 -0
  50. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/exceptions/paseto.py +0 -0
  51. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/exceptions/plugins.py +0 -0
  52. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/exceptions/sessions.py +0 -0
  53. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/ext/__init__.py +0 -0
  54. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/ext/fastapi/__init__.py +0 -0
  55. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/ext/flask/__init__.py +0 -0
  56. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/ext/flask/objects.py +0 -0
  57. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/ext/litestar/__init__.py +0 -0
  58. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/ext/litestar/objects.py +0 -0
  59. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/ext/starlette/__init__.py +0 -0
  60. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/ext/starlette/objects.py +0 -0
  61. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/jose/__algorithms__.py +0 -0
  62. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/jose/__base__.py +0 -0
  63. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/jose/jwe.py +0 -0
  64. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/jose/jwk.py +0 -0
  65. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/jose/jws.py +0 -0
  66. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/jose/lists/__base__.py +0 -0
  67. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/jose/lists/__init__.py +0 -0
  68. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/jose/lists/json.py +0 -0
  69. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/jose/lists/memory.py +0 -0
  70. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/jose/lists/redis.py +0 -0
  71. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/jose/utils.py +0 -0
  72. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/jwt/__algorithms__.py +0 -0
  73. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/jwt/__base__.py +0 -0
  74. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/jwt/__types__.py +0 -0
  75. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/jwt/lists/__base__.py +0 -0
  76. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/jwt/lists/__init__.py +0 -0
  77. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/jwt/lists/json.py +0 -0
  78. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/jwt/lists/redis.py +0 -0
  79. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/jwt/module.py +0 -0
  80. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/jwt/utils.py +0 -0
  81. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/logger.py +0 -0
  82. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/oauth2/__base__.py +0 -0
  83. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/oauth2/__init__.py +0 -0
  84. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/oauth2/builtin/__init__.py +0 -0
  85. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/oauth2/builtin/github.py +0 -0
  86. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/oauth2/builtin/gitlab.py +0 -0
  87. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/oauth2/builtin/google.py +0 -0
  88. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/oauth2/builtin/yandex.py +0 -0
  89. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/oauth2/client.py +0 -0
  90. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/otp/__base__.py +0 -0
  91. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/otp/__init__.py +0 -0
  92. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/otp/hotp.py +0 -0
  93. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/otp/totp.py +0 -0
  94. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/paseto/__base__.py +0 -0
  95. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/paseto/__init__.py +0 -0
  96. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/paseto/utils.py +0 -0
  97. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/paseto/v1.py +0 -0
  98. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/paseto/v2.py +0 -0
  99. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/paseto/v3.py +0 -0
  100. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/paseto/v4.py +0 -0
  101. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/py.typed +0 -0
  102. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/sessions/__base__.py +0 -0
  103. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/sessions/__init__.py +0 -0
  104. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/sessions/json.py +0 -0
  105. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/sessions/redis.py +0 -0
  106. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/tests/__init__.py +0 -0
  107. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/utils/__init__.py +0 -0
  108. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/utils/aes.py +0 -0
  109. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/utils/await_maybe.py +0 -0
  110. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/utils/basic_auth.py +0 -0
  111. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/utils/ed.py +0 -0
  112. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/utils/otp_keys.py +0 -0
  113. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/utils/rsa.py +0 -0
  114. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/utils/salt_hash.py +0 -0
  115. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/utils/symmetric.py +0 -0
  116. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/utils/xchacha20poly1305.py +0 -0
  117. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jam/utils/xor.py +0 -0
  118. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jamlib.egg-info/SOURCES.txt +0 -0
  119. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jamlib.egg-info/dependency_links.txt +0 -0
  120. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jamlib.egg-info/entry_points.txt +0 -0
  121. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jamlib.egg-info/requires.txt +0 -0
  122. {jamlib-3.2.0b1 → jamlib-3.2.0b3}/src/jamlib.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jamlib
3
- Version: 3.2.0b1
3
+ Version: 3.2.0b3
4
4
  Summary: Simple and universal library for authorization.
5
5
  Author-email: Makridenko Adrian <adrianmakridenko@duck.com>, Ksenia Travnikova <kseniatravnikova@duck.com>
6
6
  License: Apache-2.0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "jamlib"
3
- version = "3.2.0b1"
3
+ version = "3.2.0b3"
4
4
  description = "Simple and universal library for authorization."
5
5
  authors = [
6
6
  {name = "Makridenko Adrian",email = "adrianmakridenko@duck.com"},
@@ -6,7 +6,7 @@ from typing import Any, Literal
6
6
 
7
7
  from jam.encoders import BaseEncoder, JsonEncoder
8
8
  from jam.exceptions import JamConfigurationError
9
- from jam.jose.__base__ import BaseJWT
9
+ from jam.jose.__base__ import BaseJWE, BaseJWS, BaseJWT
10
10
  from jam.logger import BaseLogger, JamLogger
11
11
  from jam.oauth2.__base__ import BaseOAuth2Client
12
12
  from jam.otp.__base__ import BaseOTP, OTPConfig
@@ -18,7 +18,7 @@ from jam.utils.config_maker import __config_maker__, __module_loader__
18
18
  class BaseJam(ABC):
19
19
  """Base jam instance."""
20
20
 
21
- MODULES: dict[str, str] = {}
21
+ MODULES: dict[str, str | dict[str, str]] = {}
22
22
 
23
23
  def __init__(
24
24
  self,
@@ -55,6 +55,9 @@ class BaseJam(ABC):
55
55
  self._logger = logger(log_level)
56
56
  self._serializer = serializer
57
57
  self.jwt: BaseJWT | None = None
58
+ self.jws: BaseJWS | None = None
59
+ self.jwe: BaseJWE | None = None
60
+ self.jose: dict[str, Any] | None = None
58
61
  self.session: BaseSessionModule | None = None
59
62
  self.oauth2: dict[str, BaseOAuth2Client] | None = None
60
63
  self.otp: OTPConfig | None = None
@@ -69,7 +72,7 @@ class BaseJam(ABC):
69
72
  )
70
73
  self._logger.debug(
71
74
  "BaseJam initialization complete. Modules loaded:\n"
72
- f" jwt={self.jwt is not None}, session={self.session is not None}, oauth2={self.oauth2 is not None}"
75
+ f" jwt={self.jwt is not None}, jose={self.jose is not None}, session={self.session is not None}, oauth2={self.oauth2 is not None}"
73
76
  )
74
77
  gc.collect()
75
78
 
@@ -139,6 +142,7 @@ class BaseJam(ABC):
139
142
  """Build instance.
140
143
 
141
144
  Load modules from configuration and initialize them.
145
+ Supports both flat modules (name -> path) and nested modules (name -> {subname -> path}).
142
146
 
143
147
  Args:
144
148
  config (dict[str, Any]): Configuration
@@ -151,24 +155,68 @@ class BaseJam(ABC):
151
155
  self._logger.debug(f"Missing configuration for module {name}")
152
156
  continue
153
157
 
154
- try:
155
- module_cls = __module_loader__(path)
156
- self._logger.debug(f"Loading module {name} from {path}")
157
- params = config.get(name, {})
158
- self._logger.debug(
159
- f"Module {name} config params: {list(params.keys())}"
160
- )
161
- # params["logger"] = self._logger
162
- # params["serializer"] = self._serializer
163
- module_instance = module_cls(**params)
164
- self.__setattr__(name, module_instance)
165
- self._logger.debug(f"Module {name} initialized successfully")
166
-
167
- except Exception as e:
168
- self._logger.error(
169
- f"Failed to load module {name} from {path}: {e}",
170
- exc_info=True,
171
- )
158
+ if isinstance(path, dict):
159
+ subconfig = config.get(name, {})
160
+ if not isinstance(subconfig, dict):
161
+ subconfig = {}
162
+
163
+ for subname, subpath in path.items():
164
+ if subname not in subconfig:
165
+ self._logger.debug(
166
+ f"Missing configuration for module {name}.{subname}"
167
+ )
168
+ continue
169
+
170
+ try:
171
+ module_cls = __module_loader__(subpath)
172
+ self._logger.debug(
173
+ f"Loading module {name}.{subname} from {subpath}"
174
+ )
175
+ params = subconfig.get(subname, {})
176
+ self._logger.debug(
177
+ f"Module {name}.{subname} config params: {list(params.keys())}"
178
+ )
179
+ module_instance = module_cls(**params)
180
+
181
+ if self.jose is None:
182
+ self.jose = {}
183
+ self.jose[subname] = module_instance
184
+
185
+ if subname == "jwt":
186
+ self.jwt = module_instance
187
+ elif subname == "jws":
188
+ self.jws = module_instance
189
+ elif subname == "jwe":
190
+ self.jwe = module_instance
191
+
192
+ self._logger.debug(
193
+ f"Module {name}.{subname} initialized successfully"
194
+ )
195
+
196
+ except Exception as e:
197
+ self._logger.error(
198
+ f"Failed to load module {name}.{subname} from {subpath}: {e}",
199
+ exc_info=True,
200
+ )
201
+ else:
202
+ try:
203
+ module_cls = __module_loader__(path)
204
+ self._logger.debug(f"Loading module {name} from {path}")
205
+ params = config.get(name, {})
206
+ self._logger.debug(
207
+ f"Module {name} config params: {list(params.keys())}"
208
+ )
209
+ module_instance = module_cls(**params)
210
+ self.__setattr__(name, module_instance)
211
+ self._logger.debug(
212
+ f"Module {name} initialized successfully"
213
+ )
214
+
215
+ except Exception as e:
216
+ self._logger.error(
217
+ f"Failed to load module {name} from {path}: {e}",
218
+ exc_info=True,
219
+ )
172
220
 
173
221
  def __otp(
174
222
  self, type: Literal["totp", "hotp"] | None = None
@@ -240,6 +288,70 @@ class BaseJam(ABC):
240
288
  """
241
289
  raise NotImplementedError
242
290
 
291
+ @abstractmethod
292
+ def jws_sign(
293
+ self,
294
+ data: dict[str, Any] | str,
295
+ header: dict[str, Any] | None = None,
296
+ ) -> str:
297
+ """Sign data using JWS.
298
+
299
+ Args:
300
+ data: Data to sign. If dict, will be JSON encoded.
301
+ header: JWS header.
302
+
303
+ Returns:
304
+ str: JWS token.
305
+ """
306
+ raise NotImplementedError
307
+
308
+ @abstractmethod
309
+ def jws_verify(self, token: str) -> dict[str, Any]:
310
+ """Verify JWS token.
311
+
312
+ Args:
313
+ token: JWS token.
314
+
315
+ Returns:
316
+ dict[str, Any]: Decoded payload.
317
+
318
+ Raises:
319
+ JamJWSVerificationError: If verification fails.
320
+ """
321
+ raise NotImplementedError
322
+
323
+ @abstractmethod
324
+ def jwe_encrypt(
325
+ self,
326
+ data: dict[str, Any] | str,
327
+ header: dict[str, Any] | None = None,
328
+ ) -> str:
329
+ """Encrypt data using JWE.
330
+
331
+ Args:
332
+ data: Data to encrypt. If dict, will be JSON encoded.
333
+ header: JWE header.
334
+
335
+ Returns:
336
+ str: JWE token.
337
+ """
338
+ raise NotImplementedError
339
+
340
+ @abstractmethod
341
+ def jwe_decrypt(self, token: str) -> bytes:
342
+ """Decrypt JWE token.
343
+
344
+ Args:
345
+ token: JWE token.
346
+
347
+ Returns:
348
+ bytes: Decrypted data.
349
+
350
+ Raises:
351
+ JamJWEDecryptionError: If decryption fails.
352
+ """
353
+ raise NotImplementedError
354
+
243
355
  @abstractmethod
244
356
  def session_create(self, session_key: str, data: dict[str, Any]) -> str:
245
357
  """Create new session.
@@ -12,5 +12,5 @@ from jam.encoders import JsonEncoder
12
12
  from jam.instance import Jam
13
13
 
14
14
 
15
- __version__ = "3.2.0b1"
15
+ __version__ = "3.2.0b3"
16
16
  __all__ = ["Jam", "JsonEncoder", "BaseJam", "BaseEncoder"]
@@ -6,15 +6,19 @@ from typing import Any
6
6
  from jam.__base__ import BaseJam
7
7
  from jam.aio.oauth2.__base__ import BaseAsyncOAuth2Client
8
8
  from jam.aio.sessions.__base__ import BaseAsyncSessionModule
9
+ from jam.jose.__base__ import BaseJWE, BaseJWS
9
10
 
10
11
 
11
12
  class BaseAsyncJam(BaseJam):
12
13
  """Base async jam instance."""
13
14
 
14
- MODULES: dict[str, str] = {}
15
+ MODULES: dict[str, str | dict[str, str]] = {}
15
16
 
16
17
  session: BaseAsyncSessionModule | None = None # type: ignore[override]
17
18
  oauth2: dict[str, BaseAsyncOAuth2Client] | None = None # type: ignore[override]
19
+ jose: dict[str, Any] | None = None # type: ignore[override]
20
+ jws: BaseJWS | None = None # type: ignore[override]
21
+ jwe: BaseJWE | None = None # type: ignore[override]
18
22
 
19
23
  @abstractmethod
20
24
  async def jwt_make_payload( # type: ignore[override]
@@ -340,6 +344,70 @@ class BaseAsyncJam(BaseJam):
340
344
  """
341
345
  raise NotImplementedError
342
346
 
347
+ @abstractmethod
348
+ async def jws_sign(
349
+ self,
350
+ data: dict[str, Any] | str,
351
+ header: dict[str, Any] | None = None,
352
+ ) -> str:
353
+ """Sign data using JWS.
354
+
355
+ Args:
356
+ data: Data to sign. If dict, will be JSON encoded.
357
+ header: JWS header.
358
+
359
+ Returns:
360
+ str: JWS token.
361
+ """
362
+ raise NotImplementedError
363
+
364
+ @abstractmethod
365
+ async def jws_verify(self, token: str) -> dict[str, Any]:
366
+ """Verify JWS token.
367
+
368
+ Args:
369
+ token: JWS token.
370
+
371
+ Returns:
372
+ dict[str, Any]: Decoded payload.
373
+
374
+ Raises:
375
+ JamJWSVerificationError: If verification fails.
376
+ """
377
+ raise NotImplementedError
378
+
379
+ @abstractmethod
380
+ async def jwe_encrypt(
381
+ self,
382
+ data: dict[str, Any] | str,
383
+ header: dict[str, Any] | None = None,
384
+ ) -> str:
385
+ """Encrypt data using JWE.
386
+
387
+ Args:
388
+ data: Data to encrypt. If dict, will be JSON encoded.
389
+ header: JWE header.
390
+
391
+ Returns:
392
+ str: JWE token.
393
+ """
394
+ raise NotImplementedError
395
+
396
+ @abstractmethod
397
+ async def jwe_decrypt(self, token: str) -> bytes:
398
+ """Decrypt JWE token.
399
+
400
+ Args:
401
+ token: JWE token.
402
+
403
+ Returns:
404
+ bytes: Decrypted data.
405
+
406
+ Raises:
407
+ JamJWEDecryptionError: If decryption fails.
408
+ """
409
+ raise NotImplementedError
410
+
343
411
  @abstractmethod
344
412
  async def paseto_decode( # type: ignore[override]
345
413
  self, token: str, check_exp: bool = True, check_list: bool = True
@@ -18,8 +18,12 @@ from jam.exceptions import (
18
18
  class Jam(BaseAsyncJam):
19
19
  """Main async Jam instance."""
20
20
 
21
- MODULES: dict[str, str] = {
22
- "jwt": "jam.aio.jwt.create_instance",
21
+ MODULES: dict[str, str | dict[str, str]] = {
22
+ "jose": {
23
+ "jwt": "jam.jose.create_jwt_instance",
24
+ "jws": "jam.jose.create_jws_instance",
25
+ "jwe": "jam.jose.create_jwe_instance",
26
+ },
23
27
  "session": "jam.aio.sessions.create_instance",
24
28
  "oauth2": "jam.aio.oauth2.create_instance",
25
29
  "paseto": "jam.paseto.create_instance",
@@ -184,6 +188,76 @@ class Jam(BaseAsyncJam):
184
188
  return {"header": headers, "payload": payload}
185
189
  return payload
186
190
 
191
+ async def jws_sign(
192
+ self,
193
+ data: dict[str, Any] | str,
194
+ header: dict[str, Any] | None = None,
195
+ ) -> str:
196
+ """Sign data using JWS.
197
+
198
+ Args:
199
+ data: Data to sign. If dict, will be JSON encoded.
200
+ header: JWS header.
201
+
202
+ Returns:
203
+ str: JWS token.
204
+ """
205
+ assert self.jws is not None
206
+ self._logger.debug(f"Signing data with JWS, header: {header}")
207
+ token = self.jws.sign(header or {}, data)
208
+ self._logger.debug(f"JWS token created, length: {len(token)}")
209
+ return token
210
+
211
+ async def jws_verify(self, token: str) -> dict[str, Any]:
212
+ """Verify JWS token.
213
+
214
+ Args:
215
+ token: JWS token.
216
+
217
+ Returns:
218
+ dict[str, Any]: Decoded payload.
219
+ """
220
+ assert self.jws is not None
221
+ self._logger.debug(f"Verifying JWS token, length: {len(token)}")
222
+ result = self.jws.verify(token)
223
+ self._logger.debug("JWS token verified successfully")
224
+ return result
225
+
226
+ async def jwe_encrypt(
227
+ self,
228
+ data: dict[str, Any] | str,
229
+ header: dict[str, Any] | None = None,
230
+ ) -> str:
231
+ """Encrypt data using JWE.
232
+
233
+ Args:
234
+ data: Data to encrypt. If dict, will be JSON encoded.
235
+ header: JWE header.
236
+
237
+ Returns:
238
+ str: JWE token.
239
+ """
240
+ assert self.jwe is not None
241
+ self._logger.debug(f"Encrypting data with JWE, header: {header}")
242
+ token = self.jwe.encrypt(data, header)
243
+ self._logger.debug(f"JWE token created, length: {len(token)}")
244
+ return token
245
+
246
+ async def jwe_decrypt(self, token: str) -> bytes:
247
+ """Decrypt JWE token.
248
+
249
+ Args:
250
+ token: JWE token.
251
+
252
+ Returns:
253
+ bytes: Decrypted data.
254
+ """
255
+ assert self.jwe is not None
256
+ self._logger.debug(f"Decrypting JWE token, length: {len(token)}")
257
+ result = self.jwe.decrypt(token)
258
+ self._logger.debug("JWE token decrypted successfully")
259
+ return result
260
+
187
261
  async def session_create(
188
262
  self, session_key: str, data: dict[str, Any]
189
263
  ) -> str:
@@ -5,7 +5,7 @@ from typing import Any
5
5
  import flask
6
6
 
7
7
  from jam.exceptions import JamFlaskPluginConfigError
8
- from jam.jwt import create_instance as create_jwt
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
- payload = self._auth.decode(token)
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.jwt import JWT
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: JWT
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
- data = self.AUTH_MODULE.decode(token)
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.jwt import create_instance as create_jwt
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.jwt import create_instance as create_jwt
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
- user = self._user.from_payload(data)
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()
@@ -19,8 +19,12 @@ from jam.exceptions import (
19
19
  class Jam(BaseJam):
20
20
  """Main instance."""
21
21
 
22
- MODULES: dict[str, str] = {
23
- "jwt": "jam.jose.JWT",
22
+ MODULES: dict[str, str | dict[str, str]] = {
23
+ "jose": {
24
+ "jwt": "jam.jose.create_jwt_instance",
25
+ "jws": "jam.jose.create_jws_instance",
26
+ "jwe": "jam.jose.create_jwe_instance",
27
+ },
24
28
  "session": "jam.sessions.create_instance",
25
29
  "oauth2": "jam.oauth2.create_instance",
26
30
  "paseto": "jam.paseto.create_instance",
@@ -190,6 +194,79 @@ class Jam(BaseJam):
190
194
  return data
191
195
  return payload
192
196
 
197
+ def jws_sign(
198
+ self,
199
+ data: dict[str, Any] | str,
200
+ header: dict[str, Any] | None = None,
201
+ ) -> str:
202
+ """Sign data using JWS.
203
+
204
+ Args:
205
+ data: Data to sign. If dict, will be JSON encoded.
206
+ header: JWS header.
207
+
208
+ Returns:
209
+ str: JWS token.
210
+ """
211
+ assert self.jws is not None
212
+ self._logger.debug(f"Signing data with JWS, header: {header}")
213
+ token = self.jws.sign(header or {}, data)
214
+ self._logger.debug(f"JWS token created, length: {len(token)}")
215
+ return token
216
+
217
+ def jws_verify(self, token: str) -> dict[str, Any]:
218
+ """Verify JWS token.
219
+
220
+ Args:
221
+ token: JWS token.
222
+
223
+ Returns:
224
+ dict[str, Any]: Decoded payload.
225
+ """
226
+ assert self.jws is not None
227
+ self._logger.debug(f"Verifying JWS token, length: {len(token)}")
228
+ result = self.jws.verify(token)
229
+ self._logger.debug("JWS token verified successfully")
230
+ return result
231
+
232
+ def jwe_encrypt(
233
+ self,
234
+ data: dict[str, Any] | str,
235
+ header: dict[str, Any] | None = None,
236
+ ) -> str:
237
+ """Encrypt data using JWE.
238
+
239
+ Args:
240
+ data: Data to encrypt. If dict, will be JSON encoded.
241
+ header: JWE header.
242
+
243
+ Returns:
244
+ str: JWE token.
245
+ """
246
+ assert self.jwe is not None
247
+ self._logger.debug(f"Encrypting data with JWE, header: {header}")
248
+ token = self.jwe.encrypt(
249
+ self._serializer.dumps(data) if isinstance(data, dict) else data,
250
+ header,
251
+ )
252
+ self._logger.debug(f"JWE token created, length: {len(token)}")
253
+ return token
254
+
255
+ def jwe_decrypt(self, token: str) -> bytes:
256
+ """Decrypt JWE token.
257
+
258
+ Args:
259
+ token: JWE token.
260
+
261
+ Returns:
262
+ bytes: Decrypted data.
263
+ """
264
+ assert self.jwe is not None
265
+ self._logger.debug(f"Decrypting JWE token, length: {len(token)}")
266
+ result = self.jwe.decrypt(token)
267
+ self._logger.debug("JWE token decrypted successfully")
268
+ return result
269
+
193
270
  def session_create(self, session_key: str, data: dict[str, Any]) -> str:
194
271
  """Create new session.
195
272
 
@@ -0,0 +1,113 @@
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_jwt_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
+ def create_jws_instance(
46
+ alg: str,
47
+ key: Any = None,
48
+ password: bytes | None = None,
49
+ logger: BaseLogger = logger,
50
+ **kwargs: Any,
51
+ ) -> JWS:
52
+ """Create JWS instance."""
53
+ if key is None:
54
+ raise ValueError("'key' must be provided")
55
+
56
+ from jam.jose.jwk import JWK as JWKClass
57
+
58
+ if isinstance(key, JWKClass):
59
+ key = key._to_keylike()
60
+
61
+ return JWS(
62
+ alg=alg,
63
+ key=key,
64
+ password=password,
65
+ logger=logger,
66
+ )
67
+
68
+
69
+ def create_jwe_instance(
70
+ alg: str,
71
+ enc: str,
72
+ key: Any = None,
73
+ password: bytes | None = None,
74
+ serializer: BaseEncoder | type[BaseEncoder] = JsonEncoder,
75
+ logger: BaseLogger = logger,
76
+ **kwargs: Any,
77
+ ) -> JWE:
78
+ """Create JWE instance."""
79
+ if key is None:
80
+ raise ValueError("'key' must be provided")
81
+
82
+ from jam.jose.jwk import JWK as JWKClass
83
+
84
+ if isinstance(key, JWKClass):
85
+ key = key._to_keylike()
86
+
87
+ return JWE(
88
+ alg=alg,
89
+ enc=enc,
90
+ key=key,
91
+ password=password,
92
+ serializer=serializer,
93
+ logger=logger,
94
+ )
95
+
96
+
97
+ create_instance = create_jwt_instance
98
+
99
+
100
+ __all__ = [
101
+ "JWK",
102
+ "JWKSet",
103
+ "JWS",
104
+ "JWE",
105
+ "JWT",
106
+ "JWKRSA",
107
+ "JWKEC",
108
+ "JWKOct",
109
+ "create_instance",
110
+ "create_jwt_instance",
111
+ "create_jws_instance",
112
+ "create_jwe_instance",
113
+ ]
@@ -360,7 +360,7 @@ class JWT(BaseJWT):
360
360
  ) -> dict[str, Any]:
361
361
  now = int(datetime.now().timestamp())
362
362
  payload = {
363
- "tid": str(uuid4()),
363
+ "jti": str(uuid4()), # TODO: Make it replecable
364
364
  "iat": now,
365
365
  "iss": iss,
366
366
  "sub": sub,
@@ -62,4 +62,4 @@ def create_instance(
62
62
  )
63
63
 
64
64
 
65
- __all__ = ["JWT", "BaseJWT", "create_instance"]
65
+ __all__ = ["JWT", "BaseJWT", "create_instance"]
@@ -7,7 +7,7 @@ import uuid
7
7
 
8
8
  from jam.aio import Jam as AioJam
9
9
  from jam.instance import Jam
10
- from jam.jwt.utils import base64url_decode
10
+ from jam.jose.utils import __base64url_decode__ as base64url_decode
11
11
  from jam.tests.fakers import (
12
12
  fake_jwt_token,
13
13
  fake_oauth2_token,
@@ -5,7 +5,7 @@ from secrets import token_urlsafe
5
5
  from typing import Any
6
6
  import uuid
7
7
 
8
- from jam.jwt.utils import base64url_encode
8
+ from jam.jose.utils import __base64url_encode__ as base64url_encode
9
9
  from jam.utils import xor_my_data
10
10
 
11
11
 
@@ -6,6 +6,7 @@ import os
6
6
  import re
7
7
  import sys
8
8
  from typing import Any
9
+ import warnings
9
10
 
10
11
  from jam.encoders import BaseEncoder, JsonEncoder
11
12
  from jam.exceptions import JamConfigurationError
@@ -351,18 +352,48 @@ def __config_maker__(
351
352
  ext = config.split(".")[-1].lower()
352
353
  match ext:
353
354
  case "yml" | "yaml":
354
- return __yaml_config_parser(path=config, pointer=pointer).copy()
355
+ result = __yaml_config_parser(
356
+ path=config, pointer=pointer
357
+ ).copy()
355
358
  case "toml":
356
- return __toml_config_parser(path=config, pointer=pointer).copy()
359
+ result = __toml_config_parser(
360
+ path=config, pointer=pointer
361
+ ).copy()
357
362
  case "json":
358
- return __json_config_parser(path=config).copy()
363
+ result = __json_config_parser(path=config).copy()
359
364
  case _:
360
365
  raise JamConfigurationError(
361
366
  message="YML/YAML, TOML or JSON configs only!",
362
367
  error_code="configuration.invalid_config_type",
363
368
  )
364
369
  else:
365
- return config.copy()
370
+ result = config.copy()
371
+
372
+ result = __apply_jwt_config_migration__(result)
373
+ return result
374
+
375
+
376
+ # TODO: Delete this after deleting jam.jwt
377
+ def __apply_jwt_config_migration__(config: dict) -> dict:
378
+ """Backward compatibility: copy jam.jwt -> jam.jose.jwt with deprecation warning.
379
+
380
+ Args:
381
+ config: Parsed configuration dict
382
+
383
+ Returns:
384
+ dict: Updated config with migration applied
385
+ """
386
+ if "jwt" in config and "jose" not in config:
387
+ warnings.warn(
388
+ "[jam.jwt] is deprecated. Use [jam.jose.jwt] instead. See: https://jam.makridenko.ru/usage/jose",
389
+ DeprecationWarning,
390
+ stacklevel=2,
391
+ )
392
+ config["jose"] = {"jwt": config["jwt"]}
393
+ elif "jwt" in config and "jose" in config:
394
+ if "jwt" not in config.get("jose", {}):
395
+ config["jose"]["jwt"] = config["jwt"]
396
+ return config
366
397
 
367
398
 
368
399
  def __module_loader__(path: str) -> Callable:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jamlib
3
- Version: 3.2.0b1
3
+ Version: 3.2.0b3
4
4
  Summary: Simple and universal library for authorization.
5
5
  Author-email: Makridenko Adrian <adrianmakridenko@duck.com>, Ksenia Travnikova <kseniatravnikova@duck.com>
6
6
  License: Apache-2.0
@@ -1,19 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- """JOSE tools."""
4
-
5
- from jam.jose.jwk import JWK, JWKOct, JWKSet, JWKRSA, JWKEC
6
- from jam.jose.jws import JWS
7
- from jam.jose.jwe import JWE
8
- from jam.jose.jwt import JWT
9
-
10
- __all__ = [
11
- "JWK",
12
- "JWKSet",
13
- "JWS",
14
- "JWE",
15
- "JWT",
16
- "JWKRSA",
17
- "JWKEC",
18
- "JWKOct",
19
- ]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes