jamlib 3.1.0__tar.gz → 3.2.0b1__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 (121) hide show
  1. {jamlib-3.1.0/src/jamlib.egg-info → jamlib-3.2.0b1}/PKG-INFO +1 -1
  2. {jamlib-3.1.0 → jamlib-3.2.0b1}/pyproject.toml +1 -1
  3. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/__base__.py +29 -22
  4. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/__deprecated__.py +1 -1
  5. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/__init__.py +1 -1
  6. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/aio/__base__.py +36 -1
  7. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/aio/instance.py +76 -7
  8. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/exceptions/__init__.py +26 -21
  9. jamlib-3.2.0b1/src/jam/exceptions/jose.py +23 -0
  10. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/exceptions/jwt.py +9 -2
  11. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/instance.py +79 -10
  12. jamlib-3.2.0b1/src/jam/jose/__algorithms__.py +1161 -0
  13. jamlib-3.2.0b1/src/jam/jose/__base__.py +338 -0
  14. jamlib-3.2.0b1/src/jam/jose/__init__.py +19 -0
  15. jamlib-3.2.0b1/src/jam/jose/jwe.py +199 -0
  16. jamlib-3.2.0b1/src/jam/jose/jwk.py +506 -0
  17. jamlib-3.2.0b1/src/jam/jose/jws.py +192 -0
  18. jamlib-3.2.0b1/src/jam/jose/jwt.py +544 -0
  19. jamlib-3.2.0b1/src/jam/jose/lists/__base__.py +40 -0
  20. jamlib-3.2.0b1/src/jam/jose/lists/__init__.py +11 -0
  21. jamlib-3.2.0b1/src/jam/jose/lists/json.py +136 -0
  22. jamlib-3.2.0b1/src/jam/jose/lists/memory.py +111 -0
  23. jamlib-3.2.0b1/src/jam/jose/lists/redis.py +161 -0
  24. {jamlib-3.1.0/src/jam/jwt → jamlib-3.2.0b1/src/jam/jose}/utils.py +3 -2
  25. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/jwt/__base__.py +2 -0
  26. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/jwt/lists/__base__.py +3 -0
  27. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/jwt/lists/json.py +2 -0
  28. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/jwt/lists/redis.py +2 -0
  29. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/jwt/module.py +2 -0
  30. jamlib-3.2.0b1/src/jam/jwt/utils.py +36 -0
  31. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/tests/clients.py +56 -0
  32. {jamlib-3.1.0 → jamlib-3.2.0b1/src/jamlib.egg-info}/PKG-INFO +1 -1
  33. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jamlib.egg-info/SOURCES.txt +14 -0
  34. {jamlib-3.1.0 → jamlib-3.2.0b1}/LICENSE.md +0 -0
  35. {jamlib-3.1.0 → jamlib-3.2.0b1}/README.md +0 -0
  36. {jamlib-3.1.0 → jamlib-3.2.0b1}/setup.cfg +0 -0
  37. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/__base_encoder__.py +0 -0
  38. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/aio/__init__.py +0 -0
  39. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/aio/jwt/__init__.py +0 -0
  40. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/aio/oauth2/__base__.py +0 -0
  41. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/aio/oauth2/__init__.py +0 -0
  42. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/aio/oauth2/builtin/__init__.py +0 -0
  43. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/aio/oauth2/builtin/github.py +0 -0
  44. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/aio/oauth2/builtin/gitlab.py +0 -0
  45. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/aio/oauth2/builtin/google.py +0 -0
  46. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/aio/oauth2/builtin/yandex.py +0 -0
  47. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/aio/oauth2/client.py +0 -0
  48. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/aio/sessions/__base__.py +0 -0
  49. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/aio/sessions/__init__.py +0 -0
  50. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/aio/sessions/json.py +0 -0
  51. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/aio/sessions/redis.py +0 -0
  52. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/cli/__init__.py +0 -0
  53. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/cli/cli.py +0 -0
  54. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/cli/commands/__init__.py +0 -0
  55. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/cli/commands/keys.py +0 -0
  56. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/cli/commands/password.py +0 -0
  57. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/encoders.py +0 -0
  58. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/exceptions/base.py +0 -0
  59. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/exceptions/oauth2.py +0 -0
  60. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/exceptions/paseto.py +0 -0
  61. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/exceptions/plugins.py +0 -0
  62. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/exceptions/sessions.py +0 -0
  63. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/ext/__init__.py +0 -0
  64. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/ext/fastapi/__init__.py +0 -0
  65. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/ext/flask/__init__.py +0 -0
  66. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/ext/flask/extensions.py +0 -0
  67. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/ext/flask/objects.py +0 -0
  68. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/ext/litestar/__init__.py +0 -0
  69. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/ext/litestar/middleware.py +0 -0
  70. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/ext/litestar/objects.py +0 -0
  71. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/ext/litestar/plugins.py +0 -0
  72. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/ext/starlette/__init__.py +0 -0
  73. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/ext/starlette/backends.py +0 -0
  74. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/ext/starlette/objects.py +0 -0
  75. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/jwt/__algorithms__.py +0 -0
  76. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/jwt/__init__.py +0 -0
  77. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/jwt/__types__.py +0 -0
  78. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/jwt/lists/__init__.py +0 -0
  79. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/logger.py +0 -0
  80. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/oauth2/__base__.py +0 -0
  81. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/oauth2/__init__.py +0 -0
  82. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/oauth2/builtin/__init__.py +0 -0
  83. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/oauth2/builtin/github.py +0 -0
  84. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/oauth2/builtin/gitlab.py +0 -0
  85. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/oauth2/builtin/google.py +0 -0
  86. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/oauth2/builtin/yandex.py +0 -0
  87. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/oauth2/client.py +0 -0
  88. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/otp/__base__.py +0 -0
  89. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/otp/__init__.py +0 -0
  90. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/otp/hotp.py +0 -0
  91. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/otp/totp.py +0 -0
  92. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/paseto/__base__.py +0 -0
  93. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/paseto/__init__.py +0 -0
  94. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/paseto/utils.py +0 -0
  95. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/paseto/v1.py +0 -0
  96. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/paseto/v2.py +0 -0
  97. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/paseto/v3.py +0 -0
  98. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/paseto/v4.py +0 -0
  99. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/py.typed +0 -0
  100. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/sessions/__base__.py +0 -0
  101. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/sessions/__init__.py +0 -0
  102. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/sessions/json.py +0 -0
  103. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/sessions/redis.py +0 -0
  104. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/tests/__init__.py +0 -0
  105. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/tests/fakers.py +0 -0
  106. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/utils/__init__.py +0 -0
  107. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/utils/aes.py +0 -0
  108. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/utils/await_maybe.py +0 -0
  109. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/utils/basic_auth.py +0 -0
  110. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/utils/config_maker.py +0 -0
  111. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/utils/ed.py +0 -0
  112. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/utils/otp_keys.py +0 -0
  113. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/utils/rsa.py +0 -0
  114. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/utils/salt_hash.py +0 -0
  115. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/utils/symmetric.py +0 -0
  116. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/utils/xchacha20poly1305.py +0 -0
  117. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jam/utils/xor.py +0 -0
  118. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jamlib.egg-info/dependency_links.txt +0 -0
  119. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jamlib.egg-info/entry_points.txt +0 -0
  120. {jamlib-3.1.0 → jamlib-3.2.0b1}/src/jamlib.egg-info/requires.txt +0 -0
  121. {jamlib-3.1.0 → jamlib-3.2.0b1}/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.1.0
3
+ Version: 3.2.0b1
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.1.0"
3
+ version = "3.2.0b1"
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.jwt.__base__ import BaseJWT
9
+ from jam.jose.__base__ import 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
@@ -189,36 +189,41 @@ class BaseJam(ABC):
189
189
  raise JamConfigurationError(message="Unknown OTP type.")
190
190
 
191
191
  @abstractmethod
192
- def jwt_make_payload(
193
- self, exp: int | None, data: dict[str, Any]
194
- ) -> dict[str, Any]:
195
- """Make JWT-specific payload.
196
-
197
- Args:
198
- exp (int | None): Token expire, if None -> use default
199
- data (dict[str, Any]): Data to payload
200
-
201
- Returns:
202
- dict[str, Any]: Payload
203
- """
204
- raise NotImplementedError
205
-
206
- @abstractmethod
207
- def jwt_create(self, payload: dict[str, Any]) -> str:
208
- """Create JWT token.
192
+ def jwt_encode(
193
+ self,
194
+ iss: str | None = None,
195
+ sub: str | None = None,
196
+ aud: str | None = None,
197
+ exp: int | None = None,
198
+ nbf: int | None = None,
199
+ *,
200
+ payload: dict[str, Any] | None = None,
201
+ header: dict[str, Any] | None = None,
202
+ ) -> str:
203
+ """Encode the JWT with the given expire, header, and payload.
209
204
 
210
205
  Args:
211
- payload (dict[str, Any]): Data payload
206
+ exp (int | None): The expiration time in seconds.
207
+ nbf (int | None): The not-before time in seconds.
208
+ iss (str | None): The issuer.
209
+ sub (str | None): The subject.
210
+ aud (str | None): The audience.
211
+ header (dict[str, Any] | None): The header to include in the JWT.
212
+ payload (dict[str, Any] | None): The payload to include in the JWT.
212
213
 
213
214
  Returns:
214
- str: New token
215
-
215
+ str: The encoded JWT.
216
216
  """
217
217
  raise NotImplementedError
218
218
 
219
219
  @abstractmethod
220
220
  def jwt_decode(
221
- self, token: str, check_exp: bool = True, check_list: bool = True
221
+ self,
222
+ token: str,
223
+ check_exp: bool = True,
224
+ check_list: bool = True,
225
+ check_nbf: bool = False,
226
+ include_headers: bool = False,
222
227
  ) -> dict[str, Any]:
223
228
  """Verify and decode JWT token.
224
229
 
@@ -226,6 +231,8 @@ class BaseJam(ABC):
226
231
  token (str): JWT token
227
232
  check_exp (bool): Check expire
228
233
  check_list (bool): Check white/black list. Docs: https://jam.makridenko.ru/jwt/lists/what/
234
+ check_nbf (bool): Check not-before time
235
+ include_headers (bool): Include headers in the decoded payload
229
236
 
230
237
  Returns:
231
238
  dict[str, Any]: Decoded payload
@@ -8,7 +8,7 @@ def deprecated(replacement: str | None = None):
8
8
  """Mark funcs are deprecated."""
9
9
 
10
10
  def decorator(func):
11
- msg = f"Function {func.__name__}() is deprecated."
11
+ msg = f"{func.__name__}() is deprecated."
12
12
  if replacement:
13
13
  msg += f" {replacement}"
14
14
 
@@ -12,5 +12,5 @@ from jam.encoders import JsonEncoder
12
12
  from jam.instance import Jam
13
13
 
14
14
 
15
- __version__ = "3.0.0"
15
+ __version__ = "3.2.0b1"
16
16
  __all__ = ["Jam", "JsonEncoder", "BaseJam", "BaseEncoder"]
@@ -45,9 +45,42 @@ class BaseAsyncJam(BaseJam):
45
45
  """
46
46
  raise NotImplementedError
47
47
 
48
+ @abstractmethod
49
+ async def jwt_encode(
50
+ self,
51
+ iss: str | None = None,
52
+ sub: str | None = None,
53
+ aud: str | None = None,
54
+ exp: int | None = None,
55
+ nbf: int | None = None,
56
+ *,
57
+ payload: dict[str, Any] | None = None,
58
+ header: dict[str, Any] | None = None,
59
+ ) -> str:
60
+ """Encode the JWT with the given expire, header, and payload.
61
+
62
+ Args:
63
+ exp (int | None): The expiration time in seconds.
64
+ nbf (int | None): The not-before time in seconds.
65
+ iss (str | None): The issuer.
66
+ sub (str | None): The subject.
67
+ aud (str | None): The audience.
68
+ header (dict[str, Any] | None): The header to include in the JWT.
69
+ payload (dict[str, Any] | None): The payload to include in the JWT.
70
+
71
+ Returns:
72
+ str: The encoded JWT.
73
+ """
74
+ raise NotImplementedError
75
+
48
76
  @abstractmethod
49
77
  async def jwt_decode( # type: ignore[override]
50
- self, token: str, check_exp: bool = True, check_list: bool = True
78
+ self,
79
+ token: str,
80
+ check_exp: bool = True,
81
+ check_list: bool = True,
82
+ check_nbf: bool = False,
83
+ include_headers: bool = False,
51
84
  ) -> dict[str, Any]:
52
85
  """Verify and decode JWT token.
53
86
 
@@ -55,6 +88,8 @@ class BaseAsyncJam(BaseJam):
55
88
  token (str): JWT token
56
89
  check_exp (bool): Check expire
57
90
  check_list (bool): Check white/black list. Docs: https://jam.makridenko.ru/jwt/lists/what/
91
+ check_nbf (bool): Check not-before time
92
+ include_headers (bool): Include headers in the decoded payload
58
93
 
59
94
  Returns:
60
95
  dict[str, Any]: Decoded payload
@@ -1,6 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
3
  import datetime
4
+ import time
4
5
  from typing import Any
5
6
  import uuid
6
7
 
@@ -10,6 +11,7 @@ from jam.exceptions import (
10
11
  JamJWTExpired,
11
12
  JamJWTInBlackList,
12
13
  JamJWTNotInWhiteList,
14
+ JamJWTNotYetValid,
13
15
  )
14
16
 
15
17
 
@@ -68,8 +70,52 @@ class Jam(BaseAsyncJam):
68
70
 
69
71
  return token
70
72
 
73
+ async def jwt_encode(
74
+ self,
75
+ iss: str | None = None,
76
+ sub: str | None = None,
77
+ aud: str | None = None,
78
+ exp: int | None = None,
79
+ nbf: int | None = None,
80
+ *,
81
+ payload: dict[str, Any] | None = None,
82
+ header: dict[str, Any] | None = None,
83
+ ) -> str:
84
+ """Encode the JWT with the given expire, header, and payload.
85
+
86
+ Args:
87
+ exp (int | None): The expiration time in seconds.
88
+ nbf (int | None): The not-before time in seconds.
89
+ iss (str | None): The issuer.
90
+ sub (str | None): The subject.
91
+ aud (str | None): The audience.
92
+ header (dict[str, Any] | None): The header to include in the JWT.
93
+ payload (dict[str, Any] | None): The payload to include in the JWT.
94
+
95
+ Returns:
96
+ str: The encoded JWT.
97
+ """
98
+ assert self.jwt is not None
99
+ token = self.jwt.encode(
100
+ iss=iss,
101
+ sub=sub,
102
+ aud=aud,
103
+ exp=exp,
104
+ nbf=nbf,
105
+ payload=payload,
106
+ header=header,
107
+ )
108
+ if self.jwt.list and self.jwt.list.__list_type__ == "white":
109
+ self.jwt.list.add(token)
110
+ return token
111
+
71
112
  async def jwt_decode(
72
- self, token: str, check_exp: bool = True, check_list: bool = True
113
+ self,
114
+ token: str,
115
+ check_exp: bool = True,
116
+ check_list: bool = True,
117
+ check_nbf: bool = False,
118
+ include_headers: bool = False,
73
119
  ) -> dict[str, Any]:
74
120
  """Verify and decode JWT token.
75
121
 
@@ -77,23 +123,43 @@ class Jam(BaseAsyncJam):
77
123
  token (str): JWT token
78
124
  check_exp (bool): Check expire
79
125
  check_list (bool): Check white/black list. Docs: https://jam.makridenko.ru/jwt/lists/what/
126
+ check_nbf (bool): Check not-before time
127
+ include_headers (bool): Include headers in the decoded payload
80
128
 
81
129
  Returns:
82
130
  dict[str, Any]: Decoded payload
131
+
132
+ Raises:
133
+ JamJWTExpired: If token is expired
134
+ JamJWTNotYetValid: If token is not yet valid (nbf claim)
135
+ JamConfigurationError: If JWT list is not connected
136
+ JamJWTNotInWhiteList: If token is not in white list
137
+ JamJWTInBlackList: If token is in black list
83
138
  """
84
139
  assert self.jwt is not None
85
140
  self._logger.debug(
86
- f"Verifying JWT token (length: {len(token)} chars), check_exp={check_exp}, check_list={check_list}"
141
+ f"Verifying JWT token (length: {len(token)} chars), check_exp={check_exp}, check_list={check_list}, check_nbf={check_nbf}"
87
142
  )
88
- payload = self.jwt.decode(token)
143
+ data = self.jwt.decode(token)
144
+ if "payload" in data:
145
+ payload = data["payload"]
146
+ headers = data.get("header")
147
+ else:
148
+ payload = data
149
+ headers = None
150
+
151
+ if check_exp and "exp" in payload:
152
+ if payload["exp"] < time.time():
153
+ raise JamJWTExpired
154
+
155
+ if check_nbf and "nbf" in payload:
156
+ if payload["nbf"] > time.time():
157
+ raise JamJWTNotYetValid
158
+
89
159
  self._logger.debug(
90
160
  f"JWT token verified successfully, payload keys: {list(payload.keys())}"
91
161
  )
92
162
 
93
- if check_exp:
94
- if payload["exp"] < datetime.datetime.now().timestamp():
95
- raise JamJWTExpired
96
-
97
163
  if check_list:
98
164
  if not self.jwt.list:
99
165
  raise JamConfigurationError(
@@ -113,6 +179,9 @@ class Jam(BaseAsyncJam):
113
179
  message="Invalid JWT list type",
114
180
  error_code="configuration.jwt.unknown_list_type",
115
181
  )
182
+
183
+ if include_headers and headers is not None:
184
+ return {"header": headers, "payload": payload}
116
185
  return payload
117
186
 
118
187
  async def session_create(
@@ -1,49 +1,49 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
- """
4
- All Jam exceptions
5
- """
3
+ """All Jam exceptions"""
6
4
 
7
- from .base import JamError, JamConfigurationError, JamValidationError
8
-
9
- from .oauth2 import (
10
- JamOAuth2Error,
11
- JamOAuth2EmptyRaw,
12
- JamOAuth2ProviderNotConfigured,
5
+ from .base import JamConfigurationError, JamError, JamValidationError
6
+ from .jose import (
7
+ JamJWEEncryptionError,
8
+ JamJWEDecryptionError,
9
+ JamJWKValidationError,
10
+ JamJWSVerificationError,
13
11
  )
14
-
15
12
  from .jwt import (
13
+ JamJWTEmptyPrivateKey,
14
+ JamJWTEmptySecretKey,
16
15
  JamJWTExpired,
17
16
  JamJWTInBlackList,
18
17
  JamJWTNotInWhiteList,
19
- JamJWTEmptyPrivateKey,
20
- JamJWTEmptySecretKey,
18
+ JamJWTNotYetValid,
21
19
  JamJWTUnsupportedAlgorithm,
22
20
  JamJWTValidationError,
23
21
  )
24
-
22
+ from .oauth2 import (
23
+ JamOAuth2EmptyRaw,
24
+ JamOAuth2Error,
25
+ JamOAuth2ProviderNotConfigured,
26
+ )
25
27
  from .paseto import (
26
- JamPASETOInvalidSymmetricKey,
27
- JamPASETOInvalidRSAKey,
28
28
  JamPASETOInvalidED25519Key,
29
- JamPASETOInvalidSecp384r1Key,
30
- JamPASTOKeyVerificationError,
31
29
  JamPASETOInvalidPurpose,
30
+ JamPASETOInvalidRSAKey,
31
+ JamPASETOInvalidSecp384r1Key,
32
+ JamPASETOInvalidSymmetricKey,
32
33
  JamPASETOInvalidTokenFormat,
34
+ JamPASTOKeyVerificationError,
33
35
  )
34
-
35
36
  from .plugins import (
36
37
  JamFlaskPluginConfigError,
37
38
  JamFlaskPluginError,
38
39
  JamLitestarPluginConfigError,
39
40
  JamLitestarPluginError,
40
41
  JamStarlettePluginConfigError,
41
- JamStarlettePluginError
42
+ JamStarlettePluginError,
42
43
  )
43
-
44
44
  from .sessions import (
45
- JamSessionNotFound,
46
45
  JamSessionEmptyAESKey,
46
+ JamSessionNotFound,
47
47
  )
48
48
 
49
49
 
@@ -57,10 +57,15 @@ __all__ = [
57
57
  "JamJWTExpired",
58
58
  "JamJWTInBlackList",
59
59
  "JamJWTNotInWhiteList",
60
+ "JamJWTNotYetValid",
60
61
  "JamJWTEmptyPrivateKey",
61
62
  "JamJWTEmptySecretKey",
62
63
  "JamJWTUnsupportedAlgorithm",
63
64
  "JamJWTValidationError",
65
+ "JamJWSVerificationError",
66
+ "JamJWKValidationError",
67
+ "JamJWEEncryptionError",
68
+ "JamJWEDecryptionError",
64
69
  "JamPASETOInvalidSymmetricKey",
65
70
  "JamPASETOInvalidRSAKey",
66
71
  "JamPASETOInvalidED25519Key",
@@ -0,0 +1,23 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from .base import JamValidationError
4
+
5
+
6
+ class JamJWSVerificationError(JamValidationError):
7
+ default_message = "JWS signature verification failed."
8
+ default_code = "jws.verification_error"
9
+
10
+
11
+ class JamJWKValidationError(JamValidationError):
12
+ default_message = "JWK validation failed."
13
+ default_code = "jwk.validation_error"
14
+
15
+
16
+ class JamJWEEncryptionError(JamValidationError):
17
+ default_message = "JWE encryption failed."
18
+ default_code = "jwe.encryption_error"
19
+
20
+
21
+ class JamJWEDecryptionError(JamValidationError):
22
+ default_message = "JWE decryption failed."
23
+ default_code = "jwe.decryption_error"
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
- from .base import JamError, JamValidationError, JamConfigurationError
3
+ from .base import JamConfigurationError, JamError, JamValidationError
4
4
 
5
5
 
6
6
  class JamJWTExpired(JamError):
@@ -8,6 +8,11 @@ class JamJWTExpired(JamError):
8
8
  default_code = "jwt.token_expired"
9
9
 
10
10
 
11
+ class JamJWTNotYetValid(JamError):
12
+ default_message = "Token is not yet valid (nbf claim)."
13
+ default_code = "jwt.token_not_yet_valid"
14
+
15
+
11
16
  class JamJWTInBlackList(JamError):
12
17
  default_message = "Token in blacklist."
13
18
  default_code = "jwt.blacklist"
@@ -24,7 +29,9 @@ class JamJWTEmptySecretKey(JamConfigurationError):
24
29
 
25
30
 
26
31
  class JamJWTEmptyPrivateKey(JamConfigurationError):
27
- default_message = "For asymmetric encryption, you must specify `private_key`."
32
+ default_message = (
33
+ "For asymmetric encryption, you must specify `private_key`."
34
+ )
28
35
  default_code = "jwt.config.empty_private_key"
29
36
 
30
37
 
@@ -1,15 +1,18 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
3
  import datetime
4
+ import time
4
5
  from typing import Any
5
6
  import uuid
6
7
 
7
8
  from jam.__base__ import BaseJam
9
+ from jam.__deprecated__ import deprecated
8
10
  from jam.exceptions import (
9
11
  JamConfigurationError,
10
12
  JamJWTExpired,
11
13
  JamJWTInBlackList,
12
14
  JamJWTNotInWhiteList,
15
+ JamJWTNotYetValid,
13
16
  )
14
17
 
15
18
 
@@ -17,18 +20,24 @@ class Jam(BaseJam):
17
20
  """Main instance."""
18
21
 
19
22
  MODULES: dict[str, str] = {
20
- "jwt": "jam.jwt.create_instance",
23
+ "jwt": "jam.jose.JWT",
21
24
  "session": "jam.sessions.create_instance",
22
25
  "oauth2": "jam.oauth2.create_instance",
23
26
  "paseto": "jam.paseto.create_instance",
24
27
  "otp": "jam.otp.__base__.OTPConfig",
25
28
  }
26
29
 
30
+ @deprecated(
31
+ "This method is deprecated; the JWT payload is generated automatically in accordance with the specification."
32
+ )
27
33
  def jwt_make_payload(
28
34
  self, exp: int | None, data: dict[str, Any]
29
35
  ) -> dict[str, Any]:
30
36
  """Make JWT-specific payload.
31
37
 
38
+ !!! Deprecated
39
+ This method is deprecated; the JWT payload is generated automatically in accordance with the specification.
40
+
32
41
  Args:
33
42
  exp (int | None): Token expire
34
43
  data (dict[str, Any]): Data to payload
@@ -44,9 +53,13 @@ class Jam(BaseJam):
44
53
  payload = payload | data
45
54
  return payload
46
55
 
56
+ @deprecated("Use jam.jwt_encode")
47
57
  def jwt_create(self, payload: dict[str, Any]) -> str:
48
58
  """Create JWT token.
49
59
 
60
+ !!! Deprecated
61
+ Use Jam.jwt_encode
62
+
50
63
  Args:
51
64
  payload (dict[str, Any]): Data payload
52
65
 
@@ -68,8 +81,52 @@ class Jam(BaseJam):
68
81
 
69
82
  return token
70
83
 
84
+ def jwt_encode(
85
+ self,
86
+ iss: str | None = None,
87
+ sub: str | None = None,
88
+ aud: str | None = None,
89
+ exp: int | None = None,
90
+ nbf: int | None = None,
91
+ *,
92
+ payload: dict[str, Any] | None = None,
93
+ header: dict[str, Any] | None = None,
94
+ ) -> str:
95
+ """Encode the JWT with the given expire, header, and payload.
96
+
97
+ Args:
98
+ exp (int | None): The expiration time in seconds.
99
+ nbf (int | None): The not-before time in seconds.
100
+ iss (str | None): The issuer.
101
+ sub (str | None): The subject.
102
+ aud (str | None): The audience.
103
+ header (dict[str, Any] | None): The header to include in the JWT.
104
+ payload (dict[str, Any] | None): The payload to include in the JWT.
105
+
106
+ Returns:
107
+ str: The encoded JWT.
108
+ """
109
+ assert self.jwt is not None
110
+ token = self.jwt.encode(
111
+ iss=iss,
112
+ sub=sub,
113
+ aud=aud,
114
+ exp=exp,
115
+ nbf=nbf,
116
+ payload=payload,
117
+ header=header,
118
+ )
119
+ if self.jwt.list and self.jwt.list.__list_type__ == "white":
120
+ self.jwt.list.add(token)
121
+ return token
122
+
71
123
  def jwt_decode(
72
- self, token: str, check_exp: bool = True, check_list: bool = True
124
+ self,
125
+ token: str,
126
+ check_exp: bool = True,
127
+ check_list: bool = True,
128
+ check_nbf: bool = False,
129
+ include_headers: bool = False,
73
130
  ) -> dict[str, Any]:
74
131
  """Verify and decode JWT token.
75
132
 
@@ -77,29 +134,38 @@ class Jam(BaseJam):
77
134
  token (str): JWT token
78
135
  check_exp (bool): Check expire
79
136
  check_list (bool): Check white/black list. Docs: https://jam.makridenko.ru/jwt/lists/what/
137
+ check_nbf (bool): Check not-before time
138
+ include_headers (bool): Include headers in the decoded payload
80
139
 
81
140
  Returns:
82
141
  dict[str, Any]: Decoded payload
83
142
 
84
143
  Raises:
85
144
  JamJWTExpired: If token is expired
145
+ JamJWTNotYetValid: If token is not yet valid (nbf claim)
86
146
  JamConfigurationError: If JWT list is not connected
87
147
  JamJWTNotInWhiteList: If token is not in white list
88
148
  JamJWTInBlackList: If token is in black list
89
149
  """
90
150
  self._logger.debug(
91
- f"Verifying JWT token (length: {len(token)} chars), check_exp={check_exp}, check_list={check_list}"
151
+ f"Verifying JWT token (length: {len(token)} chars), check_exp={check_exp}, check_list={check_list}, check_nbf={check_nbf}"
92
152
  )
93
153
  assert self.jwt is not None
94
- payload = self.jwt.decode(token)
154
+ data = self.jwt.decode(token)
155
+ payload = data["payload"]
156
+
157
+ if check_exp and "exp" in payload:
158
+ if payload["exp"] < time.time():
159
+ raise JamJWTExpired
160
+
161
+ if check_nbf and "nbf" in payload:
162
+ if payload["nbf"] > time.time():
163
+ raise JamJWTNotYetValid
164
+
95
165
  self._logger.debug(
96
166
  f"JWT token verified successfully, payload keys: {list(payload.keys())}"
97
167
  )
98
168
 
99
- if check_exp:
100
- if payload["exp"] < datetime.datetime.now().timestamp():
101
- raise JamJWTExpired
102
-
103
169
  if check_list:
104
170
  if not self.jwt.list:
105
171
  raise JamConfigurationError(
@@ -119,6 +185,9 @@ class Jam(BaseJam):
119
185
  message="Invalid JWT list type",
120
186
  error_code="configuration.jwt.unknown_list_type",
121
187
  )
188
+
189
+ if include_headers:
190
+ return data
122
191
  return payload
123
192
 
124
193
  def session_create(self, session_key: str, data: dict[str, Any]) -> str:
@@ -391,9 +460,9 @@ class Jam(BaseJam):
391
460
  Returns:
392
461
  dict: Payload
393
462
  """
394
- from jam.paseto.utils import payload_maker
463
+ from jam.paseto.utils import payload_maker as pm
395
464
 
396
- return payload_maker(expire=exp, data=data)
465
+ return pm(expire=exp, data=data)
397
466
 
398
467
  def paseto_create(
399
468
  self,