jamlib 3.2.0b2__tar.gz → 3.2.0.post0__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 (127) hide show
  1. {jamlib-3.2.0b2/src/jamlib.egg-info → jamlib-3.2.0.post0}/PKG-INFO +5 -5
  2. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/README.md +3 -3
  3. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/pyproject.toml +2 -2
  4. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/__base__.py +187 -27
  5. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/__init__.py +2 -4
  6. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/aio/__base__.py +84 -2
  7. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/aio/instance.py +101 -10
  8. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/aio/sessions/__base__.py +5 -1
  9. jamlib-3.2.0.post0/src/jam/exceptions/jose.py +73 -0
  10. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/ext/flask/extensions.py +2 -0
  11. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/ext/litestar/plugins.py +3 -0
  12. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/ext/starlette/backends.py +2 -0
  13. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/instance.py +87 -5
  14. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jose/__algorithms__.py +156 -54
  15. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jose/__base__.py +116 -36
  16. jamlib-3.2.0.post0/src/jam/jose/__init__.py +113 -0
  17. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jose/jwe.py +11 -6
  18. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jose/jwk.py +31 -10
  19. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jose/jws.py +20 -1
  20. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jose/jwt.py +143 -79
  21. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jose/lists/redis.py +4 -1
  22. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/logger.py +8 -1
  23. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/paseto/v1.py +4 -0
  24. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/paseto/v2.py +4 -0
  25. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/paseto/v3.py +4 -0
  26. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/paseto/v4.py +4 -0
  27. jamlib-3.2.0.post0/src/jam/plugins/__base__.py +58 -0
  28. jamlib-3.2.0.post0/src/jam/plugins/__init__.py +10 -0
  29. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/sessions/__base__.py +5 -1
  30. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/tests/clients.py +271 -53
  31. jamlib-3.2.0.post0/src/jam/tests/fakers.py +187 -0
  32. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/utils/config_maker.py +50 -4
  33. jamlib-3.2.0.post0/src/jam/utils/version_check.py +18 -0
  34. {jamlib-3.2.0b2 → jamlib-3.2.0.post0/src/jamlib.egg-info}/PKG-INFO +5 -5
  35. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jamlib.egg-info/SOURCES.txt +3 -0
  36. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jamlib.egg-info/requires.txt +1 -1
  37. jamlib-3.2.0b2/src/jam/exceptions/jose.py +0 -23
  38. jamlib-3.2.0b2/src/jam/jose/__init__.py +0 -55
  39. jamlib-3.2.0b2/src/jam/tests/fakers.py +0 -102
  40. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/LICENSE.md +0 -0
  41. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/setup.cfg +0 -0
  42. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/__base_encoder__.py +0 -0
  43. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/__deprecated__.py +0 -0
  44. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/aio/__init__.py +0 -0
  45. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/aio/jwt/__init__.py +0 -0
  46. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/aio/oauth2/__base__.py +0 -0
  47. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/aio/oauth2/__init__.py +0 -0
  48. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/aio/oauth2/builtin/__init__.py +0 -0
  49. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/aio/oauth2/builtin/github.py +0 -0
  50. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/aio/oauth2/builtin/gitlab.py +0 -0
  51. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/aio/oauth2/builtin/google.py +0 -0
  52. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/aio/oauth2/builtin/yandex.py +0 -0
  53. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/aio/oauth2/client.py +0 -0
  54. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/aio/sessions/__init__.py +0 -0
  55. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/aio/sessions/json.py +0 -0
  56. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/aio/sessions/redis.py +0 -0
  57. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/cli/__init__.py +0 -0
  58. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/cli/cli.py +0 -0
  59. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/cli/commands/__init__.py +0 -0
  60. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/cli/commands/keys.py +0 -0
  61. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/cli/commands/password.py +0 -0
  62. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/encoders.py +0 -0
  63. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/exceptions/__init__.py +0 -0
  64. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/exceptions/base.py +0 -0
  65. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/exceptions/jwt.py +0 -0
  66. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/exceptions/oauth2.py +0 -0
  67. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/exceptions/paseto.py +0 -0
  68. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/exceptions/plugins.py +0 -0
  69. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/exceptions/sessions.py +0 -0
  70. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/ext/__init__.py +0 -0
  71. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/ext/fastapi/__init__.py +0 -0
  72. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/ext/flask/__init__.py +0 -0
  73. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/ext/flask/objects.py +0 -0
  74. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/ext/litestar/__init__.py +0 -0
  75. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/ext/litestar/middleware.py +0 -0
  76. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/ext/litestar/objects.py +0 -0
  77. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/ext/starlette/__init__.py +0 -0
  78. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/ext/starlette/objects.py +0 -0
  79. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jose/lists/__base__.py +0 -0
  80. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jose/lists/__init__.py +0 -0
  81. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jose/lists/json.py +0 -0
  82. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jose/lists/memory.py +0 -0
  83. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jose/utils.py +0 -0
  84. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jwt/__algorithms__.py +0 -0
  85. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jwt/__base__.py +0 -0
  86. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jwt/__init__.py +0 -0
  87. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jwt/__types__.py +0 -0
  88. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jwt/lists/__base__.py +0 -0
  89. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jwt/lists/__init__.py +0 -0
  90. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jwt/lists/json.py +0 -0
  91. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jwt/lists/redis.py +0 -0
  92. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jwt/module.py +0 -0
  93. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jwt/utils.py +0 -0
  94. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/oauth2/__base__.py +0 -0
  95. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/oauth2/__init__.py +0 -0
  96. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/oauth2/builtin/__init__.py +0 -0
  97. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/oauth2/builtin/github.py +0 -0
  98. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/oauth2/builtin/gitlab.py +0 -0
  99. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/oauth2/builtin/google.py +0 -0
  100. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/oauth2/builtin/yandex.py +0 -0
  101. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/oauth2/client.py +0 -0
  102. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/otp/__base__.py +0 -0
  103. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/otp/__init__.py +0 -0
  104. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/otp/hotp.py +0 -0
  105. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/otp/totp.py +0 -0
  106. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/paseto/__base__.py +0 -0
  107. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/paseto/__init__.py +0 -0
  108. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/paseto/utils.py +0 -0
  109. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/py.typed +0 -0
  110. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/sessions/__init__.py +0 -0
  111. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/sessions/json.py +0 -0
  112. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/sessions/redis.py +0 -0
  113. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/tests/__init__.py +0 -0
  114. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/utils/__init__.py +0 -0
  115. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/utils/aes.py +0 -0
  116. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/utils/await_maybe.py +0 -0
  117. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/utils/basic_auth.py +0 -0
  118. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/utils/ed.py +0 -0
  119. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/utils/otp_keys.py +0 -0
  120. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/utils/rsa.py +0 -0
  121. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/utils/salt_hash.py +0 -0
  122. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/utils/symmetric.py +0 -0
  123. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/utils/xchacha20poly1305.py +0 -0
  124. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/utils/xor.py +0 -0
  125. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jamlib.egg-info/dependency_links.txt +0 -0
  126. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jamlib.egg-info/entry_points.txt +0 -0
  127. {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/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.0.post0
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
@@ -77,7 +77,7 @@ from jam import Jam
77
77
 
78
78
  jam = Jam(config="config.toml")
79
79
 
80
- jwt = jam.jwt_create({"user": 1})
80
+ jwt = jam.jwt_encode(payload={"user": 1})
81
81
  session_id = jam.session_create(session_key="username", data={"user": 1})
82
82
  otp_code = jam.otp_code(secret="3DB7FOAOFBCI3WFDRE7EPF43CA")
83
83
  ```
@@ -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/)
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 | ✅ | ✅ | ❌ | ❌ | ❌ |
@@ -24,7 +24,7 @@ from jam import Jam
24
24
 
25
25
  jam = Jam(config="config.toml")
26
26
 
27
- jwt = jam.jwt_create({"user": 1})
27
+ jwt = jam.jwt_encode(payload={"user": 1})
28
28
  session_id = jam.session_create(session_key="username", data={"user": 1})
29
29
  otp_code = jam.otp_code(secret="3DB7FOAOFBCI3WFDRE7EPF43CA")
30
30
  ```
@@ -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/)
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.0.post0"
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.0"
14
+ __all__ = ["Jam", "BaseJam"]
@@ -4,24 +4,35 @@ from abc import abstractmethod
4
4
  from typing import Any
5
5
 
6
6
  from jam.__base__ import BaseJam
7
+ from jam.__deprecated__ import deprecated
7
8
  from jam.aio.oauth2.__base__ import BaseAsyncOAuth2Client
8
9
  from jam.aio.sessions.__base__ import BaseAsyncSessionModule
10
+ from jam.jose.__base__ import BaseJWE, BaseJWS
9
11
 
10
12
 
11
13
  class BaseAsyncJam(BaseJam):
12
14
  """Base async jam instance."""
13
15
 
14
- MODULES: dict[str, str] = {}
16
+ MODULES: dict[str, str | dict[str, str]] = {}
15
17
 
16
18
  session: BaseAsyncSessionModule | None = None # type: ignore[override]
17
19
  oauth2: dict[str, BaseAsyncOAuth2Client] | None = None # type: ignore[override]
20
+ jose: dict[str, Any] | None = None # type: ignore[override]
21
+ jws: BaseJWS | None = None # type: ignore[override]
22
+ jwe: BaseJWE | None = None # type: ignore[override]
18
23
 
19
24
  @abstractmethod
25
+ @deprecated(
26
+ "This method is deprecated; the JWT payload is generated automatically in accordance with the specification."
27
+ )
20
28
  async def jwt_make_payload( # type: ignore[override]
21
29
  self, exp: int | None, data: dict[str, Any]
22
30
  ) -> dict[str, Any]:
23
31
  """Make JWT-specific payload.
24
32
 
33
+ !!! Deprecated
34
+ This method is deprecated; the JWT payload is generated automatically in accordance with the specification.
35
+
25
36
  Args:
26
37
  exp (int | None): Token expire, if None -> use default
27
38
  data (dict[str, Any]): Data to payload
@@ -32,6 +43,7 @@ class BaseAsyncJam(BaseJam):
32
43
  raise NotImplementedError
33
44
 
34
45
  @abstractmethod
46
+ @deprecated("Use jam.jwt_encode")
35
47
  async def jwt_create( # type: ignore[override]
36
48
  self, payload: dict[str, Any]
37
49
  ) -> str:
@@ -46,13 +58,14 @@ class BaseAsyncJam(BaseJam):
46
58
  raise NotImplementedError
47
59
 
48
60
  @abstractmethod
49
- async def jwt_encode(
61
+ async def jwt_encode( # type: ignore[override]
50
62
  self,
51
63
  iss: str | None = None,
52
64
  sub: str | None = None,
53
65
  aud: str | None = None,
54
66
  exp: int | None = None,
55
67
  nbf: int | None = None,
68
+ jti: str | None = None,
56
69
  *,
57
70
  payload: dict[str, Any] | None = None,
58
71
  header: dict[str, Any] | None = None,
@@ -65,6 +78,7 @@ class BaseAsyncJam(BaseJam):
65
78
  iss (str | None): The issuer.
66
79
  sub (str | None): The subject.
67
80
  aud (str | None): The audience.
81
+ jti (str | None): The JWT ID. If none use the JTI fabric function.
68
82
  header (dict[str, Any] | None): The header to include in the JWT.
69
83
  payload (dict[str, Any] | None): The payload to include in the JWT.
70
84
 
@@ -340,6 +354,74 @@ class BaseAsyncJam(BaseJam):
340
354
  """
341
355
  raise NotImplementedError
342
356
 
357
+ @abstractmethod
358
+ async def jws_sign( # type: ignore[override]
359
+ self,
360
+ data: dict[str, Any] | str,
361
+ header: dict[str, Any] | None = None,
362
+ ) -> str:
363
+ """Sign data using JWS.
364
+
365
+ Args:
366
+ data: Data to sign. If dict, will be JSON encoded.
367
+ header: JWS header.
368
+
369
+ Returns:
370
+ str: JWS token.
371
+ """
372
+ raise NotImplementedError
373
+
374
+ @abstractmethod
375
+ async def jws_verify( # type: ignore[override]
376
+ self, token: str
377
+ ) -> dict[str, Any]:
378
+ """Verify JWS token.
379
+
380
+ Args:
381
+ token: JWS token.
382
+
383
+ Returns:
384
+ dict[str, Any]: Decoded payload.
385
+
386
+ Raises:
387
+ JamJWSVerificationError: If verification fails.
388
+ """
389
+ raise NotImplementedError
390
+
391
+ @abstractmethod
392
+ async def jwe_encrypt( # type: ignore[override]
393
+ self,
394
+ data: dict[str, Any] | str,
395
+ header: dict[str, Any] | None = None,
396
+ ) -> str:
397
+ """Encrypt data using JWE.
398
+
399
+ Args:
400
+ data: Data to encrypt. If dict, will be JSON encoded.
401
+ header: JWE header.
402
+
403
+ Returns:
404
+ str: JWE token.
405
+ """
406
+ raise NotImplementedError
407
+
408
+ @abstractmethod
409
+ async def jwe_decrypt( # type: ignore[override]
410
+ self, token: str
411
+ ) -> bytes:
412
+ """Decrypt JWE token.
413
+
414
+ Args:
415
+ token: JWE token.
416
+
417
+ Returns:
418
+ bytes: Decrypted data.
419
+
420
+ Raises:
421
+ JamJWEDecryptionError: If decryption fails.
422
+ """
423
+ raise NotImplementedError
424
+
343
425
  @abstractmethod
344
426
  async def paseto_decode( # type: ignore[override]
345
427
  self, token: str, check_exp: bool = True, check_list: bool = True