jamlib 3.2.0b2__tar.gz → 3.2.0rc0__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 (126) hide show
  1. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/PKG-INFO +4 -4
  2. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/README.md +2 -2
  3. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/pyproject.toml +2 -2
  4. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/__base__.py +187 -27
  5. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/__init__.py +2 -4
  6. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/aio/__base__.py +76 -2
  7. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/aio/instance.py +81 -2
  8. jamlib-3.2.0rc0/src/jam/exceptions/jose.py +73 -0
  9. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/instance.py +87 -5
  10. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/jose/__algorithms__.py +156 -54
  11. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/jose/__base__.py +100 -25
  12. jamlib-3.2.0rc0/src/jam/jose/__init__.py +113 -0
  13. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/jose/jwe.py +11 -6
  14. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/jose/jwk.py +31 -10
  15. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/jose/jws.py +16 -1
  16. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/jose/jwt.py +132 -70
  17. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/jose/lists/redis.py +4 -1
  18. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/logger.py +8 -1
  19. jamlib-3.2.0rc0/src/jam/plugins/__base__.py +58 -0
  20. jamlib-3.2.0rc0/src/jam/plugins/__init__.py +10 -0
  21. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/utils/config_maker.py +35 -4
  22. jamlib-3.2.0rc0/src/jam/utils/version_check.py +18 -0
  23. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jamlib.egg-info/PKG-INFO +4 -4
  24. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jamlib.egg-info/SOURCES.txt +3 -0
  25. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jamlib.egg-info/requires.txt +1 -1
  26. jamlib-3.2.0b2/src/jam/exceptions/jose.py +0 -23
  27. jamlib-3.2.0b2/src/jam/jose/__init__.py +0 -55
  28. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/LICENSE.md +0 -0
  29. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/setup.cfg +0 -0
  30. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/__base_encoder__.py +0 -0
  31. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/__deprecated__.py +0 -0
  32. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/aio/__init__.py +0 -0
  33. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/aio/jwt/__init__.py +0 -0
  34. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/aio/oauth2/__base__.py +0 -0
  35. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/aio/oauth2/__init__.py +0 -0
  36. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/aio/oauth2/builtin/__init__.py +0 -0
  37. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/aio/oauth2/builtin/github.py +0 -0
  38. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/aio/oauth2/builtin/gitlab.py +0 -0
  39. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/aio/oauth2/builtin/google.py +0 -0
  40. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/aio/oauth2/builtin/yandex.py +0 -0
  41. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/aio/oauth2/client.py +0 -0
  42. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/aio/sessions/__base__.py +0 -0
  43. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/aio/sessions/__init__.py +0 -0
  44. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/aio/sessions/json.py +0 -0
  45. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/aio/sessions/redis.py +0 -0
  46. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/cli/__init__.py +0 -0
  47. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/cli/cli.py +0 -0
  48. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/cli/commands/__init__.py +0 -0
  49. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/cli/commands/keys.py +0 -0
  50. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/cli/commands/password.py +0 -0
  51. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/encoders.py +0 -0
  52. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/exceptions/__init__.py +0 -0
  53. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/exceptions/base.py +0 -0
  54. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/exceptions/jwt.py +0 -0
  55. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/exceptions/oauth2.py +0 -0
  56. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/exceptions/paseto.py +0 -0
  57. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/exceptions/plugins.py +0 -0
  58. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/exceptions/sessions.py +0 -0
  59. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/ext/__init__.py +0 -0
  60. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/ext/fastapi/__init__.py +0 -0
  61. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/ext/flask/__init__.py +0 -0
  62. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/ext/flask/extensions.py +0 -0
  63. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/ext/flask/objects.py +0 -0
  64. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/ext/litestar/__init__.py +0 -0
  65. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/ext/litestar/middleware.py +0 -0
  66. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/ext/litestar/objects.py +0 -0
  67. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/ext/litestar/plugins.py +0 -0
  68. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/ext/starlette/__init__.py +0 -0
  69. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/ext/starlette/backends.py +0 -0
  70. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/ext/starlette/objects.py +0 -0
  71. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/jose/lists/__base__.py +0 -0
  72. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/jose/lists/__init__.py +0 -0
  73. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/jose/lists/json.py +0 -0
  74. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/jose/lists/memory.py +0 -0
  75. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/jose/utils.py +0 -0
  76. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/jwt/__algorithms__.py +0 -0
  77. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/jwt/__base__.py +0 -0
  78. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/jwt/__init__.py +0 -0
  79. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/jwt/__types__.py +0 -0
  80. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/jwt/lists/__base__.py +0 -0
  81. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/jwt/lists/__init__.py +0 -0
  82. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/jwt/lists/json.py +0 -0
  83. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/jwt/lists/redis.py +0 -0
  84. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/jwt/module.py +0 -0
  85. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/jwt/utils.py +0 -0
  86. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/oauth2/__base__.py +0 -0
  87. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/oauth2/__init__.py +0 -0
  88. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/oauth2/builtin/__init__.py +0 -0
  89. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/oauth2/builtin/github.py +0 -0
  90. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/oauth2/builtin/gitlab.py +0 -0
  91. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/oauth2/builtin/google.py +0 -0
  92. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/oauth2/builtin/yandex.py +0 -0
  93. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/oauth2/client.py +0 -0
  94. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/otp/__base__.py +0 -0
  95. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/otp/__init__.py +0 -0
  96. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/otp/hotp.py +0 -0
  97. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/otp/totp.py +0 -0
  98. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/paseto/__base__.py +0 -0
  99. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/paseto/__init__.py +0 -0
  100. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/paseto/utils.py +0 -0
  101. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/paseto/v1.py +0 -0
  102. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/paseto/v2.py +0 -0
  103. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/paseto/v3.py +0 -0
  104. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/paseto/v4.py +0 -0
  105. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/py.typed +0 -0
  106. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/sessions/__base__.py +0 -0
  107. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/sessions/__init__.py +0 -0
  108. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/sessions/json.py +0 -0
  109. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/sessions/redis.py +0 -0
  110. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/tests/__init__.py +0 -0
  111. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/tests/clients.py +0 -0
  112. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/tests/fakers.py +0 -0
  113. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/utils/__init__.py +0 -0
  114. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/utils/aes.py +0 -0
  115. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/utils/await_maybe.py +0 -0
  116. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/utils/basic_auth.py +0 -0
  117. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/utils/ed.py +0 -0
  118. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/utils/otp_keys.py +0 -0
  119. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/utils/rsa.py +0 -0
  120. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/utils/salt_hash.py +0 -0
  121. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/utils/symmetric.py +0 -0
  122. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/utils/xchacha20poly1305.py +0 -0
  123. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jam/utils/xor.py +0 -0
  124. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jamlib.egg-info/dependency_links.txt +0 -0
  125. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/src/jamlib.egg-info/entry_points.txt +0 -0
  126. {jamlib-3.2.0b2 → jamlib-3.2.0rc0}/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.0b2
3
+ Version: 3.2.0rc0
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
@@ -30,7 +30,7 @@ Classifier: Framework :: FastAPI
30
30
  Requires-Python: >=3.10
31
31
  Description-Content-Type: text/markdown
32
32
  License-File: LICENSE.md
33
- Requires-Dist: cryptography>=46.0.5
33
+ Requires-Dist: cryptography>=48.0.0
34
34
  Provides-Extra: cli
35
35
  Requires-Dist: click>=8.3.1; extra == "cli"
36
36
  Provides-Extra: redis
@@ -85,7 +85,7 @@ otp_code = jam.otp_code(secret="3DB7FOAOFBCI3WFDRE7EPF43CA")
85
85
  ## Why Jam?
86
86
  Jam is a library that provides the most popular AUTH* mechanisms right out of the box.
87
87
 
88
- * [JWT](https://jam.makridenko.ru/usage/jwt/)
88
+ * [JOSE](https://jam.makridenko.ru/usage/jose/index/)
89
89
  * [PASETO](https://jam.makridenko.ru/usage/paseto/)
90
90
  * [Server side sessions](https://jam.makridenko.ru/usage/sessions/)
91
91
  * [OTP](https://jam.makridenko.ru/usage/otp/)
@@ -109,7 +109,7 @@ Here is a comparison with other libraries:
109
109
 
110
110
  | Features / Library | **Jam** | [Authx](https://authx.yezz.me/) | [PyJWT](https://pyjwt.readthedocs.io) | [AuthLib](https://docs.authlib.org) | [OTP Auth](https://otp.authlib.org/) |
111
111
  |-----------------------|--------|----------------------------------|---------------------------------------|-------------------------------------|--------------------------------------|
112
- | JWT | ✅ | | | ✅ | ❌ |
112
+ | JOSE | ✅ | ❌ only JWT | ❌ only JWT | ✅ | ❌ |
113
113
  | JWT black/white lists | ✅ | ❌ | ❌ | ❌ | ❌ |
114
114
  | PASETO | ✅ | ❌ | ❌ | ❌ | ❌ |
115
115
  | Server side sessions | ✅ | ✅ | ❌ | ❌ | ❌ |
@@ -32,7 +32,7 @@ otp_code = jam.otp_code(secret="3DB7FOAOFBCI3WFDRE7EPF43CA")
32
32
  ## Why Jam?
33
33
  Jam is a library that provides the most popular AUTH* mechanisms right out of the box.
34
34
 
35
- * [JWT](https://jam.makridenko.ru/usage/jwt/)
35
+ * [JOSE](https://jam.makridenko.ru/usage/jose/index/)
36
36
  * [PASETO](https://jam.makridenko.ru/usage/paseto/)
37
37
  * [Server side sessions](https://jam.makridenko.ru/usage/sessions/)
38
38
  * [OTP](https://jam.makridenko.ru/usage/otp/)
@@ -56,7 +56,7 @@ Here is a comparison with other libraries:
56
56
 
57
57
  | Features / Library | **Jam** | [Authx](https://authx.yezz.me/) | [PyJWT](https://pyjwt.readthedocs.io) | [AuthLib](https://docs.authlib.org) | [OTP Auth](https://otp.authlib.org/) |
58
58
  |-----------------------|--------|----------------------------------|---------------------------------------|-------------------------------------|--------------------------------------|
59
- | JWT | ✅ | | | ✅ | ❌ |
59
+ | JOSE | ✅ | ❌ only JWT | ❌ only JWT | ✅ | ❌ |
60
60
  | JWT black/white lists | ✅ | ❌ | ❌ | ❌ | ❌ |
61
61
  | PASETO | ✅ | ❌ | ❌ | ❌ | ❌ |
62
62
  | Server side sessions | ✅ | ✅ | ❌ | ❌ | ❌ |
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "jamlib"
3
- version = "3.2.0b2"
3
+ version = "3.2.0rc0"
4
4
  description = "Simple and universal library for authorization."
5
5
  authors = [
6
6
  {name = "Makridenko Adrian",email = "adrianmakridenko@duck.com"},
@@ -23,7 +23,7 @@ keywords = [
23
23
  ]
24
24
  requires-python = ">=3.10"
25
25
  dependencies = [
26
- "cryptography>=46.0.5",
26
+ "cryptography>=48.0.0",
27
27
  ]
28
28
 
29
29
  classifiers = [
@@ -1,16 +1,21 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  from abc import ABC, abstractmethod
4
6
  import gc
7
+ import os
5
8
  from typing import Any, Literal
6
9
 
7
- from jam.encoders import BaseEncoder, JsonEncoder
10
+ from jam.__base_encoder__ import BaseEncoder
11
+ from jam.encoders import JsonEncoder
8
12
  from jam.exceptions import JamConfigurationError
9
- from jam.jose.__base__ import BaseJWT
13
+ from jam.jose.__base__ import BaseJWE, BaseJWS, BaseJWT
10
14
  from jam.logger import BaseLogger, JamLogger
11
15
  from jam.oauth2.__base__ import BaseOAuth2Client
12
16
  from jam.otp.__base__ import BaseOTP, OTPConfig
13
17
  from jam.paseto.__base__ import BasePASETO
18
+ from jam.plugins.__base__ import BasePlugin
14
19
  from jam.sessions.__base__ import BaseSessionModule
15
20
  from jam.utils.config_maker import __config_maker__, __module_loader__
16
21
 
@@ -18,7 +23,7 @@ from jam.utils.config_maker import __config_maker__, __module_loader__
18
23
  class BaseJam(ABC):
19
24
  """Base jam instance."""
20
25
 
21
- MODULES: dict[str, str] = {}
26
+ MODULES: dict[str, str | dict[str, str]] = {}
22
27
 
23
28
  def __init__(
24
29
  self,
@@ -30,15 +35,17 @@ class BaseJam(ABC):
30
35
  "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"
31
36
  ] = "INFO",
32
37
  serializer: BaseEncoder | type[BaseEncoder] = JsonEncoder,
38
+ plugins: list[type[BasePlugin]] = [],
33
39
  ) -> None:
34
40
  """Initialize instance.
35
41
 
36
42
  Args:
37
- config (Union[str, dict[str, Any]]): Configuration
38
- pointer (str): Pointer
39
- logger (BaseLogger): Logger
40
- log_level (Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]): Log level
41
- serializer (Union[BaseEncoder, type[BaseBrowser]]): Serializer
43
+ config (Union[str, dict[str, Any]]): Configuration
44
+ pointer (str): Pointer
45
+ logger (BaseLogger): Logger
46
+ log_level (Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]): Log level
47
+ serializer (Union[BaseEncoder, type[BaseBrowser]]): Serializer
48
+ plugins (list[type[BasePlugin]]): List of plugins
42
49
 
43
50
  Returns:
44
51
  None
@@ -54,7 +61,12 @@ class BaseJam(ABC):
54
61
 
55
62
  self._logger = logger(log_level)
56
63
  self._serializer = serializer
64
+ self._plugins = []
65
+
57
66
  self.jwt: BaseJWT | None = None
67
+ self.jws: BaseJWS | None = None
68
+ self.jwe: BaseJWE | None = None
69
+ self.jose: dict[str, Any] | None = None
58
70
  self.session: BaseSessionModule | None = None
59
71
  self.oauth2: dict[str, BaseOAuth2Client] | None = None
60
72
  self.otp: OTPConfig | None = None
@@ -69,8 +81,11 @@ class BaseJam(ABC):
69
81
  )
70
82
  self._logger.debug(
71
83
  "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}"
84
+ 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
85
  )
86
+ if os.getenv("JAM_ENABLE_PLUGINS", "0") == "1":
87
+ self._logger.warning("Experimental plugins are enabled!")
88
+ self.__setup_plugins(plugins)
74
89
  gc.collect()
75
90
 
76
91
  def __build_main_config(
@@ -139,6 +154,7 @@ class BaseJam(ABC):
139
154
  """Build instance.
140
155
 
141
156
  Load modules from configuration and initialize them.
157
+ Supports both flat modules (name -> path) and nested modules (name -> {subname -> path}).
142
158
 
143
159
  Args:
144
160
  config (dict[str, Any]): Configuration
@@ -151,24 +167,68 @@ class BaseJam(ABC):
151
167
  self._logger.debug(f"Missing configuration for module {name}")
152
168
  continue
153
169
 
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
- )
170
+ if isinstance(path, dict):
171
+ subconfig = config.get(name, {})
172
+ if not isinstance(subconfig, dict):
173
+ subconfig = {}
174
+
175
+ for subname, subpath in path.items():
176
+ if subname not in subconfig:
177
+ self._logger.debug(
178
+ f"Missing configuration for module {name}.{subname}"
179
+ )
180
+ continue
181
+
182
+ try:
183
+ module_cls = __module_loader__(subpath)
184
+ self._logger.debug(
185
+ f"Loading module {name}.{subname} from {subpath}"
186
+ )
187
+ params = subconfig.get(subname, {})
188
+ self._logger.debug(
189
+ f"Module {name}.{subname} config params: {list(params.keys())}"
190
+ )
191
+ module_instance = module_cls(**params)
192
+
193
+ if self.jose is None:
194
+ self.jose = {}
195
+ self.jose[subname] = module_instance
196
+
197
+ if subname == "jwt":
198
+ self.jwt = module_instance
199
+ elif subname == "jws":
200
+ self.jws = module_instance
201
+ elif subname == "jwe":
202
+ self.jwe = module_instance
203
+
204
+ self._logger.debug(
205
+ f"Module {name}.{subname} initialized successfully"
206
+ )
207
+
208
+ except Exception as e:
209
+ self._logger.error(
210
+ f"Failed to load module {name}.{subname} from {subpath}: {e}",
211
+ exc_info=True,
212
+ )
213
+ else:
214
+ try:
215
+ module_cls = __module_loader__(path)
216
+ self._logger.debug(f"Loading module {name} from {path}")
217
+ params = config.get(name, {})
218
+ self._logger.debug(
219
+ f"Module {name} config params: {list(params.keys())}"
220
+ )
221
+ module_instance = module_cls(**params)
222
+ self.__setattr__(name, module_instance)
223
+ self._logger.debug(
224
+ f"Module {name} initialized successfully"
225
+ )
226
+
227
+ except Exception as e:
228
+ self._logger.error(
229
+ f"Failed to load module {name} from {path}: {e}",
230
+ exc_info=True,
231
+ )
172
232
 
173
233
  def __otp(
174
234
  self, type: Literal["totp", "hotp"] | None = None
@@ -188,6 +248,40 @@ class BaseJam(ABC):
188
248
  case _:
189
249
  raise JamConfigurationError(message="Unknown OTP type.")
190
250
 
251
+ def __setup_plugins(self, plugins: list[type[BasePlugin]]) -> None:
252
+ """Setup plugins."""
253
+ for plugin in plugins:
254
+ self._logger.debug(f"Setup plugin: {plugin.name}")
255
+ from jam.utils.version_check import __is_compatible__
256
+
257
+ if not __is_compatible__(None, plugin.jam_requires):
258
+ continue
259
+
260
+ _plugin = plugin(self)
261
+ _plugin.setup()
262
+ self._plugins.append(_plugin)
263
+
264
+ def emit(self, event: str, **kwargs) -> Any:
265
+ """Emit event.
266
+
267
+ Args:
268
+ event (str): Event name,
269
+ **kwargs: Event data
270
+ """
271
+ for plugin in self._plugins:
272
+ handler = getattr(plugin, f"on_{event}", None)
273
+
274
+ if handler:
275
+ try:
276
+ result = handler(**kwargs)
277
+ if isinstance(result, dict):
278
+ kwargs.update(result)
279
+
280
+ except Exception as e:
281
+ self._logger.error(f"Plugin:{plugin.name} | error: {e}")
282
+
283
+ return kwargs
284
+
191
285
  @abstractmethod
192
286
  def jwt_encode(
193
287
  self,
@@ -196,6 +290,7 @@ class BaseJam(ABC):
196
290
  aud: str | None = None,
197
291
  exp: int | None = None,
198
292
  nbf: int | None = None,
293
+ jti: str | None = None,
199
294
  *,
200
295
  payload: dict[str, Any] | None = None,
201
296
  header: dict[str, Any] | None = None,
@@ -208,6 +303,7 @@ class BaseJam(ABC):
208
303
  iss (str | None): The issuer.
209
304
  sub (str | None): The subject.
210
305
  aud (str | None): The audience.
306
+ jti (str | None): The JWT ID. If none use the JTI fabric function.
211
307
  header (dict[str, Any] | None): The header to include in the JWT.
212
308
  payload (dict[str, Any] | None): The payload to include in the JWT.
213
309
 
@@ -240,6 +336,70 @@ class BaseJam(ABC):
240
336
  """
241
337
  raise NotImplementedError
242
338
 
339
+ @abstractmethod
340
+ def jws_sign(
341
+ self,
342
+ data: dict[str, Any] | str,
343
+ header: dict[str, Any] | None = None,
344
+ ) -> str:
345
+ """Sign data using JWS.
346
+
347
+ Args:
348
+ data: Data to sign. If dict, will be JSON encoded.
349
+ header: JWS header.
350
+
351
+ Returns:
352
+ str: JWS token.
353
+ """
354
+ raise NotImplementedError
355
+
356
+ @abstractmethod
357
+ def jws_verify(self, token: str) -> dict[str, Any]:
358
+ """Verify JWS token.
359
+
360
+ Args:
361
+ token: JWS token.
362
+
363
+ Returns:
364
+ dict[str, Any]: Decoded payload.
365
+
366
+ Raises:
367
+ JamJWSVerificationError: If verification fails.
368
+ """
369
+ raise NotImplementedError
370
+
371
+ @abstractmethod
372
+ def jwe_encrypt(
373
+ self,
374
+ data: dict[str, Any] | str,
375
+ header: dict[str, Any] | None = None,
376
+ ) -> str:
377
+ """Encrypt data using JWE.
378
+
379
+ Args:
380
+ data: Data to encrypt. If dict, will be JSON encoded.
381
+ header: JWE header.
382
+
383
+ Returns:
384
+ str: JWE token.
385
+ """
386
+ raise NotImplementedError
387
+
388
+ @abstractmethod
389
+ def jwe_decrypt(self, token: str) -> bytes:
390
+ """Decrypt JWE token.
391
+
392
+ Args:
393
+ token: JWE token.
394
+
395
+ Returns:
396
+ bytes: Decrypted data.
397
+
398
+ Raises:
399
+ JamJWEDecryptionError: If decryption fails.
400
+ """
401
+ raise NotImplementedError
402
+
243
403
  @abstractmethod
244
404
  def session_create(self, session_key: str, data: dict[str, Any]) -> str:
245
405
  """Create new session.
@@ -7,10 +7,8 @@ Documentation: https://jam.makridenko.ru
7
7
  """
8
8
 
9
9
  from jam.__base__ import BaseJam
10
- from jam.__base_encoder__ import BaseEncoder
11
- from jam.encoders import JsonEncoder
12
10
  from jam.instance import Jam
13
11
 
14
12
 
15
- __version__ = "3.2.0b2"
16
- __all__ = ["Jam", "JsonEncoder", "BaseJam", "BaseEncoder"]
13
+ __version__ = "3.2.0rc0"
14
+ __all__ = ["Jam", "BaseJam"]
@@ -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]
@@ -46,13 +50,14 @@ class BaseAsyncJam(BaseJam):
46
50
  raise NotImplementedError
47
51
 
48
52
  @abstractmethod
49
- async def jwt_encode(
53
+ async def jwt_encode( # type: ignore[override]
50
54
  self,
51
55
  iss: str | None = None,
52
56
  sub: str | None = None,
53
57
  aud: str | None = None,
54
58
  exp: int | None = None,
55
59
  nbf: int | None = None,
60
+ jti: str | None = None,
56
61
  *,
57
62
  payload: dict[str, Any] | None = None,
58
63
  header: dict[str, Any] | None = None,
@@ -65,6 +70,7 @@ class BaseAsyncJam(BaseJam):
65
70
  iss (str | None): The issuer.
66
71
  sub (str | None): The subject.
67
72
  aud (str | None): The audience.
73
+ jti (str | None): The JWT ID. If none use the JTI fabric function.
68
74
  header (dict[str, Any] | None): The header to include in the JWT.
69
75
  payload (dict[str, Any] | None): The payload to include in the JWT.
70
76
 
@@ -340,6 +346,74 @@ class BaseAsyncJam(BaseJam):
340
346
  """
341
347
  raise NotImplementedError
342
348
 
349
+ @abstractmethod
350
+ async def jws_sign( # type: ignore[override]
351
+ self,
352
+ data: dict[str, Any] | str,
353
+ header: dict[str, Any] | None = None,
354
+ ) -> str:
355
+ """Sign data using JWS.
356
+
357
+ Args:
358
+ data: Data to sign. If dict, will be JSON encoded.
359
+ header: JWS header.
360
+
361
+ Returns:
362
+ str: JWS token.
363
+ """
364
+ raise NotImplementedError
365
+
366
+ @abstractmethod
367
+ async def jws_verify( # type: ignore[override]
368
+ self, token: str
369
+ ) -> dict[str, Any]:
370
+ """Verify JWS token.
371
+
372
+ Args:
373
+ token: JWS token.
374
+
375
+ Returns:
376
+ dict[str, Any]: Decoded payload.
377
+
378
+ Raises:
379
+ JamJWSVerificationError: If verification fails.
380
+ """
381
+ raise NotImplementedError
382
+
383
+ @abstractmethod
384
+ async def jwe_encrypt( # type: ignore[override]
385
+ self,
386
+ data: dict[str, Any] | str,
387
+ header: dict[str, Any] | None = None,
388
+ ) -> str:
389
+ """Encrypt data using JWE.
390
+
391
+ Args:
392
+ data: Data to encrypt. If dict, will be JSON encoded.
393
+ header: JWE header.
394
+
395
+ Returns:
396
+ str: JWE token.
397
+ """
398
+ raise NotImplementedError
399
+
400
+ @abstractmethod
401
+ async def jwe_decrypt( # type: ignore[override]
402
+ self, token: str
403
+ ) -> bytes:
404
+ """Decrypt JWE token.
405
+
406
+ Args:
407
+ token: JWE token.
408
+
409
+ Returns:
410
+ bytes: Decrypted data.
411
+
412
+ Raises:
413
+ JamJWEDecryptionError: If decryption fails.
414
+ """
415
+ raise NotImplementedError
416
+
343
417
  @abstractmethod
344
418
  async def paseto_decode( # type: ignore[override]
345
419
  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",
@@ -77,6 +81,7 @@ class Jam(BaseAsyncJam):
77
81
  aud: str | None = None,
78
82
  exp: int | None = None,
79
83
  nbf: int | None = None,
84
+ jti: str | None = None,
80
85
  *,
81
86
  payload: dict[str, Any] | None = None,
82
87
  header: dict[str, Any] | None = None,
@@ -89,6 +94,7 @@ class Jam(BaseAsyncJam):
89
94
  iss (str | None): The issuer.
90
95
  sub (str | None): The subject.
91
96
  aud (str | None): The audience.
97
+ jti (str | None): The JWT ID. If none use the JTI fabric function.
92
98
  header (dict[str, Any] | None): The header to include in the JWT.
93
99
  payload (dict[str, Any] | None): The payload to include in the JWT.
94
100
 
@@ -96,12 +102,15 @@ class Jam(BaseAsyncJam):
96
102
  str: The encoded JWT.
97
103
  """
98
104
  assert self.jwt is not None
105
+ if not jti:
106
+ jti = self.jwt.jti
99
107
  token = self.jwt.encode(
100
108
  iss=iss,
101
109
  sub=sub,
102
110
  aud=aud,
103
111
  exp=exp,
104
112
  nbf=nbf,
113
+ jti=jti,
105
114
  payload=payload,
106
115
  header=header,
107
116
  )
@@ -184,6 +193,76 @@ class Jam(BaseAsyncJam):
184
193
  return {"header": headers, "payload": payload}
185
194
  return payload
186
195
 
196
+ async def jws_sign(
197
+ self,
198
+ data: dict[str, Any] | str,
199
+ header: dict[str, Any] | None = None,
200
+ ) -> str:
201
+ """Sign data using JWS.
202
+
203
+ Args:
204
+ data: Data to sign. If dict, will be JSON encoded.
205
+ header: JWS header.
206
+
207
+ Returns:
208
+ str: JWS token.
209
+ """
210
+ assert self.jws is not None
211
+ self._logger.debug(f"Signing data with JWS, header: {header}")
212
+ token = self.jws.sign(header or {}, data)
213
+ self._logger.debug(f"JWS token created, length: {len(token)}")
214
+ return token
215
+
216
+ async def jws_verify(self, token: str) -> dict[str, Any]:
217
+ """Verify JWS token.
218
+
219
+ Args:
220
+ token: JWS token.
221
+
222
+ Returns:
223
+ dict[str, Any]: Decoded payload.
224
+ """
225
+ assert self.jws is not None
226
+ self._logger.debug(f"Verifying JWS token, length: {len(token)}")
227
+ result = self.jws.verify(token)
228
+ self._logger.debug("JWS token verified successfully")
229
+ return result
230
+
231
+ async def jwe_encrypt(
232
+ self,
233
+ data: dict[str, Any] | str,
234
+ header: dict[str, Any] | None = None,
235
+ ) -> str:
236
+ """Encrypt data using JWE.
237
+
238
+ Args:
239
+ data: Data to encrypt. If dict, will be JSON encoded.
240
+ header: JWE header.
241
+
242
+ Returns:
243
+ str: JWE token.
244
+ """
245
+ assert self.jwe is not None
246
+ self._logger.debug(f"Encrypting data with JWE, header: {header}")
247
+ token = self.jwe.encrypt(data, header)
248
+ self._logger.debug(f"JWE token created, length: {len(token)}")
249
+ return token
250
+
251
+ async def jwe_decrypt(self, token: str) -> bytes:
252
+ """Decrypt JWE token.
253
+
254
+ Args:
255
+ token: JWE token.
256
+
257
+ Returns:
258
+ bytes: Decrypted data.
259
+ """
260
+ assert self.jwe is not None
261
+ self._logger.debug(f"Decrypting JWE token, length: {len(token)}")
262
+ result = self.jwe.decrypt(token)
263
+ self._logger.debug("JWE token decrypted successfully")
264
+ return result
265
+
187
266
  async def session_create(
188
267
  self, session_key: str, data: dict[str, Any]
189
268
  ) -> str: