jamlib 3.2.0b0__tar.gz → 3.2.0b2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. {jamlib-3.2.0b0/src/jamlib.egg-info → jamlib-3.2.0b2}/PKG-INFO +1 -1
  2. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/pyproject.toml +1 -1
  3. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/__init__.py +1 -1
  4. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/aio/__base__.py +8 -1
  5. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/aio/instance.py +37 -7
  6. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/exceptions/__init__.py +2 -0
  7. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/exceptions/jwt.py +5 -0
  8. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/ext/flask/extensions.py +3 -2
  9. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/ext/litestar/middleware.py +4 -3
  10. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/ext/litestar/plugins.py +1 -1
  11. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/ext/starlette/backends.py +3 -2
  12. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/instance.py +16 -9
  13. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jose/__base__.py +3 -13
  14. jamlib-3.2.0b2/src/jam/jose/__init__.py +55 -0
  15. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jose/jwe.py +2 -0
  16. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jose/jwk.py +2 -0
  17. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jose/jwt.py +165 -119
  18. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jwt/__init__.py +1 -1
  19. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/tests/clients.py +1 -1
  20. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/tests/fakers.py +1 -1
  21. {jamlib-3.2.0b0 → jamlib-3.2.0b2/src/jamlib.egg-info}/PKG-INFO +1 -1
  22. jamlib-3.2.0b0/src/jam/jose/__init__.py +0 -19
  23. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/LICENSE.md +0 -0
  24. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/README.md +0 -0
  25. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/setup.cfg +0 -0
  26. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/__base__.py +0 -0
  27. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/__base_encoder__.py +0 -0
  28. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/__deprecated__.py +0 -0
  29. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/aio/__init__.py +0 -0
  30. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/aio/jwt/__init__.py +0 -0
  31. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/aio/oauth2/__base__.py +0 -0
  32. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/aio/oauth2/__init__.py +0 -0
  33. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/aio/oauth2/builtin/__init__.py +0 -0
  34. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/aio/oauth2/builtin/github.py +0 -0
  35. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/aio/oauth2/builtin/gitlab.py +0 -0
  36. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/aio/oauth2/builtin/google.py +0 -0
  37. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/aio/oauth2/builtin/yandex.py +0 -0
  38. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/aio/oauth2/client.py +0 -0
  39. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/aio/sessions/__base__.py +0 -0
  40. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/aio/sessions/__init__.py +0 -0
  41. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/aio/sessions/json.py +0 -0
  42. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/aio/sessions/redis.py +0 -0
  43. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/cli/__init__.py +0 -0
  44. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/cli/cli.py +0 -0
  45. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/cli/commands/__init__.py +0 -0
  46. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/cli/commands/keys.py +0 -0
  47. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/cli/commands/password.py +0 -0
  48. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/encoders.py +0 -0
  49. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/exceptions/base.py +0 -0
  50. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/exceptions/jose.py +0 -0
  51. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/exceptions/oauth2.py +0 -0
  52. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/exceptions/paseto.py +0 -0
  53. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/exceptions/plugins.py +0 -0
  54. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/exceptions/sessions.py +0 -0
  55. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/ext/__init__.py +0 -0
  56. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/ext/fastapi/__init__.py +0 -0
  57. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/ext/flask/__init__.py +0 -0
  58. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/ext/flask/objects.py +0 -0
  59. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/ext/litestar/__init__.py +0 -0
  60. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/ext/litestar/objects.py +0 -0
  61. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/ext/starlette/__init__.py +0 -0
  62. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/ext/starlette/objects.py +0 -0
  63. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jose/__algorithms__.py +0 -0
  64. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jose/jws.py +0 -0
  65. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jose/lists/__base__.py +0 -0
  66. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jose/lists/__init__.py +0 -0
  67. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jose/lists/json.py +0 -0
  68. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jose/lists/memory.py +0 -0
  69. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jose/lists/redis.py +0 -0
  70. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jose/utils.py +0 -0
  71. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jwt/__algorithms__.py +0 -0
  72. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jwt/__base__.py +0 -0
  73. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jwt/__types__.py +0 -0
  74. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jwt/lists/__base__.py +0 -0
  75. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jwt/lists/__init__.py +0 -0
  76. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jwt/lists/json.py +0 -0
  77. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jwt/lists/redis.py +0 -0
  78. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jwt/module.py +0 -0
  79. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/jwt/utils.py +0 -0
  80. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/logger.py +0 -0
  81. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/oauth2/__base__.py +0 -0
  82. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/oauth2/__init__.py +0 -0
  83. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/oauth2/builtin/__init__.py +0 -0
  84. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/oauth2/builtin/github.py +0 -0
  85. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/oauth2/builtin/gitlab.py +0 -0
  86. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/oauth2/builtin/google.py +0 -0
  87. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/oauth2/builtin/yandex.py +0 -0
  88. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/oauth2/client.py +0 -0
  89. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/otp/__base__.py +0 -0
  90. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/otp/__init__.py +0 -0
  91. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/otp/hotp.py +0 -0
  92. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/otp/totp.py +0 -0
  93. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/paseto/__base__.py +0 -0
  94. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/paseto/__init__.py +0 -0
  95. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/paseto/utils.py +0 -0
  96. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/paseto/v1.py +0 -0
  97. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/paseto/v2.py +0 -0
  98. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/paseto/v3.py +0 -0
  99. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/paseto/v4.py +0 -0
  100. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/py.typed +0 -0
  101. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/sessions/__base__.py +0 -0
  102. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/sessions/__init__.py +0 -0
  103. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/sessions/json.py +0 -0
  104. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/sessions/redis.py +0 -0
  105. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/tests/__init__.py +0 -0
  106. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/utils/__init__.py +0 -0
  107. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/utils/aes.py +0 -0
  108. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/utils/await_maybe.py +0 -0
  109. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/utils/basic_auth.py +0 -0
  110. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/utils/config_maker.py +0 -0
  111. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/utils/ed.py +0 -0
  112. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/utils/otp_keys.py +0 -0
  113. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/utils/rsa.py +0 -0
  114. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/utils/salt_hash.py +0 -0
  115. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/utils/symmetric.py +0 -0
  116. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/utils/xchacha20poly1305.py +0 -0
  117. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jam/utils/xor.py +0 -0
  118. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jamlib.egg-info/SOURCES.txt +0 -0
  119. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jamlib.egg-info/dependency_links.txt +0 -0
  120. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jamlib.egg-info/entry_points.txt +0 -0
  121. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/src/jamlib.egg-info/requires.txt +0 -0
  122. {jamlib-3.2.0b0 → jamlib-3.2.0b2}/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.0b0
3
+ Version: 3.2.0b2
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.0b0"
3
+ version = "3.2.0b2"
4
4
  description = "Simple and universal library for authorization."
5
5
  authors = [
6
6
  {name = "Makridenko Adrian",email = "adrianmakridenko@duck.com"},
@@ -12,5 +12,5 @@ from jam.encoders import JsonEncoder
12
12
  from jam.instance import Jam
13
13
 
14
14
 
15
- __version__ = "3.2.0b0"
15
+ __version__ = "3.2.0b2"
16
16
  __all__ = ["Jam", "JsonEncoder", "BaseJam", "BaseEncoder"]
@@ -75,7 +75,12 @@ class BaseAsyncJam(BaseJam):
75
75
 
76
76
  @abstractmethod
77
77
  async def jwt_decode( # type: ignore[override]
78
- 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,
79
84
  ) -> dict[str, Any]:
80
85
  """Verify and decode JWT token.
81
86
 
@@ -83,6 +88,8 @@ class BaseAsyncJam(BaseJam):
83
88
  token (str): JWT token
84
89
  check_exp (bool): Check expire
85
90
  check_list (bool): Check white/black list. Docs: https://jam.makridenko.ru/jwt/lists/what/
91
+ check_nbf (bool): Check not-before time
92
+ include_headers (bool): Include headers in the decoded payload
86
93
 
87
94
  Returns:
88
95
  dict[str, Any]: Decoded payload
@@ -1,6 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
3
  import datetime
4
+ import time
4
5
  from typing import Any
5
6
  import uuid
6
7
 
@@ -10,6 +11,7 @@ from jam.exceptions import (
10
11
  JamJWTExpired,
11
12
  JamJWTInBlackList,
12
13
  JamJWTNotInWhiteList,
14
+ JamJWTNotYetValid,
13
15
  )
14
16
 
15
17
 
@@ -108,7 +110,12 @@ class Jam(BaseAsyncJam):
108
110
  return token
109
111
 
110
112
  async def jwt_decode(
111
- self, 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,
112
119
  ) -> dict[str, Any]:
113
120
  """Verify and decode JWT token.
114
121
 
@@ -116,23 +123,43 @@ class Jam(BaseAsyncJam):
116
123
  token (str): JWT token
117
124
  check_exp (bool): Check expire
118
125
  check_list (bool): Check white/black list. Docs: https://jam.makridenko.ru/jwt/lists/what/
126
+ check_nbf (bool): Check not-before time
127
+ include_headers (bool): Include headers in the decoded payload
119
128
 
120
129
  Returns:
121
130
  dict[str, Any]: Decoded payload
131
+
132
+ Raises:
133
+ JamJWTExpired: If token is expired
134
+ JamJWTNotYetValid: If token is not yet valid (nbf claim)
135
+ JamConfigurationError: If JWT list is not connected
136
+ JamJWTNotInWhiteList: If token is not in white list
137
+ JamJWTInBlackList: If token is in black list
122
138
  """
123
139
  assert self.jwt is not None
124
140
  self._logger.debug(
125
- f"Verifying JWT token (length: {len(token)} chars), check_exp={check_exp}, check_list={check_list}"
141
+ f"Verifying JWT token (length: {len(token)} chars), check_exp={check_exp}, check_list={check_list}, check_nbf={check_nbf}"
126
142
  )
127
- 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
+
128
159
  self._logger.debug(
129
160
  f"JWT token verified successfully, payload keys: {list(payload.keys())}"
130
161
  )
131
162
 
132
- if check_exp:
133
- if payload["exp"] < datetime.datetime.now().timestamp():
134
- raise JamJWTExpired
135
-
136
163
  if check_list:
137
164
  if not self.jwt.list:
138
165
  raise JamConfigurationError(
@@ -152,6 +179,9 @@ class Jam(BaseAsyncJam):
152
179
  message="Invalid JWT list type",
153
180
  error_code="configuration.jwt.unknown_list_type",
154
181
  )
182
+
183
+ if include_headers and headers is not None:
184
+ return {"header": headers, "payload": payload}
155
185
  return payload
156
186
 
157
187
  async def session_create(
@@ -15,6 +15,7 @@ from .jwt import (
15
15
  JamJWTExpired,
16
16
  JamJWTInBlackList,
17
17
  JamJWTNotInWhiteList,
18
+ JamJWTNotYetValid,
18
19
  JamJWTUnsupportedAlgorithm,
19
20
  JamJWTValidationError,
20
21
  )
@@ -56,6 +57,7 @@ __all__ = [
56
57
  "JamJWTExpired",
57
58
  "JamJWTInBlackList",
58
59
  "JamJWTNotInWhiteList",
60
+ "JamJWTNotYetValid",
59
61
  "JamJWTEmptyPrivateKey",
60
62
  "JamJWTEmptySecretKey",
61
63
  "JamJWTUnsupportedAlgorithm",
@@ -8,6 +8,11 @@ class JamJWTExpired(JamError):
8
8
  default_code = "jwt.token_expired"
9
9
 
10
10
 
11
+ class JamJWTNotYetValid(JamError):
12
+ default_message = "Token is not yet valid (nbf claim)."
13
+ default_code = "jwt.token_not_yet_valid"
14
+
15
+
11
16
  class JamJWTInBlackList(JamError):
12
17
  default_message = "Token in blacklist."
13
18
  default_code = "jwt.blacklist"
@@ -5,7 +5,7 @@ from typing import Any
5
5
  import flask
6
6
 
7
7
  from jam.exceptions import JamFlaskPluginConfigError
8
- from jam.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()
@@ -1,6 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
3
  import datetime
4
+ import time
4
5
  from typing import Any
5
6
  import uuid
6
7
 
@@ -8,8 +9,10 @@ from jam.__base__ import BaseJam
8
9
  from jam.__deprecated__ import deprecated
9
10
  from jam.exceptions import (
10
11
  JamConfigurationError,
12
+ JamJWTExpired,
11
13
  JamJWTInBlackList,
12
14
  JamJWTNotInWhiteList,
15
+ JamJWTNotYetValid,
13
16
  )
14
17
 
15
18
 
@@ -139,24 +142,25 @@ class Jam(BaseJam):
139
142
 
140
143
  Raises:
141
144
  JamJWTExpired: If token is expired
145
+ JamJWTNotYetValid: If token is not yet valid (nbf claim)
142
146
  JamConfigurationError: If JWT list is not connected
143
147
  JamJWTNotInWhiteList: If token is not in white list
144
148
  JamJWTInBlackList: If token is in black list
145
149
  """
146
150
  self._logger.debug(
147
- f"Verifying JWT token (length: {len(token)} chars), check_exp={check_exp}, check_list={check_list}"
151
+ f"Verifying JWT token (length: {len(token)} chars), check_exp={check_exp}, check_list={check_list}, check_nbf={check_nbf}"
148
152
  )
149
153
  assert self.jwt is not None
150
- data = self.jwt.decode(token, include_headers)
154
+ data = self.jwt.decode(token)
155
+ payload = data["payload"]
151
156
 
152
- if include_headers:
153
- payload = data
154
- else:
155
- payload = data
156
- if isinstance(payload, bytes):
157
- import json
157
+ if check_exp and "exp" in payload:
158
+ if payload["exp"] < time.time():
159
+ raise JamJWTExpired
158
160
 
159
- payload = json.loads(payload)
161
+ if check_nbf and "nbf" in payload:
162
+ if payload["nbf"] > time.time():
163
+ raise JamJWTNotYetValid
160
164
 
161
165
  self._logger.debug(
162
166
  f"JWT token verified successfully, payload keys: {list(payload.keys())}"
@@ -181,6 +185,9 @@ class Jam(BaseJam):
181
185
  message="Invalid JWT list type",
182
186
  error_code="configuration.jwt.unknown_list_type",
183
187
  )
188
+
189
+ if include_headers:
190
+ return data
184
191
  return payload
185
192
 
186
193
  def session_create(self, session_key: str, data: dict[str, Any]) -> str:
@@ -65,21 +65,11 @@ class BaseJWT(ABC):
65
65
  raise NotImplementedError
66
66
 
67
67
  @abstractmethod
68
- def decode(
69
- self,
70
- token: str,
71
- include_headers: bool = False,
72
- ) -> dict[str, Any]:
73
- """Decode the JWT and return the payload.
74
-
75
- Args:
76
- token (str): The JWT to decode.
77
- exp (bool): Whether to check the expiration time. Defaults to False.
78
- nbf (bool): Whether to check the not-before time. Defaults to False.
79
- include_headers (bool): Whether to include the headers in the result. Defaults to False.
68
+ def decode(self, token: str) -> dict[str, Any]:
69
+ """Decode the JWT and return the header and payload.
80
70
 
81
71
  Returns:
82
- dict[str, Any]: The decoded payload.
72
+ dict with 'header' and 'payload' keys (both dicts).
83
73
  """
84
74
  raise NotImplementedError
85
75
 
@@ -0,0 +1,55 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ """JOSE tools."""
4
+
5
+ from typing import Any
6
+
7
+ from jam.jose.jwk import JWK, JWKOct, JWKSet, JWKRSA, JWKEC
8
+ from jam.jose.jws import JWS
9
+ from jam.jose.jwe import JWE
10
+ from jam.jose.jwt import JWT
11
+ from jam.logger import BaseLogger, logger
12
+ from jam.encoders import BaseEncoder, JsonEncoder
13
+
14
+
15
+ def create_instance(
16
+ alg: str,
17
+ secret: Any = None,
18
+ secret_key: Any = None,
19
+ password: str | bytes | None = None,
20
+ list: dict[str, Any] | None = None,
21
+ logger: BaseLogger = logger,
22
+ serializer: BaseEncoder | type[BaseEncoder] = JsonEncoder,
23
+ **kwargs: Any,
24
+ ) -> JWT:
25
+ """Create JWT instance."""
26
+ if secret is None and secret_key is not None:
27
+ secret = secret_key
28
+ elif secret is None:
29
+ raise ValueError("Either 'secret' or 'secret_key' must be provided")
30
+
31
+ key = secret
32
+ if isinstance(secret, JWK):
33
+ key = secret._to_keylike()
34
+
35
+ return JWT(
36
+ alg=alg,
37
+ secret_key=key,
38
+ password=password,
39
+ list=list,
40
+ serializer=serializer,
41
+ logger=logger,
42
+ )
43
+
44
+
45
+ __all__ = [
46
+ "JWK",
47
+ "JWKSet",
48
+ "JWS",
49
+ "JWE",
50
+ "JWT",
51
+ "JWKRSA",
52
+ "JWKEC",
53
+ "JWKOct",
54
+ "create_instance",
55
+ ]
@@ -31,10 +31,12 @@ class JWE(BaseJWE):
31
31
  Provides encryption and decryption of data using JWK keys.
32
32
 
33
33
  Example:
34
+ ```python
34
35
  >>> jwk = JWK.from_dict({"kty": "oct", "k": "your-secret-key"})
35
36
  >>> jwe = JWE(alg="A128KW", enc="A128CBC-HS256", key=jwk)
36
37
  >>> token = jwe.encrypt("secret data")
37
38
  >>> plaintext = jwe.decrypt(token)
39
+ ```
38
40
  """
39
41
 
40
42
  def __init__(
@@ -142,9 +142,11 @@ class JWK(BaseJWK):
142
142
  Provides JWK validation and signing capabilities.
143
143
 
144
144
  Example:
145
+ ```python
145
146
  >>> jwk = JWK.from_dict({"kty": "oct", "k": "your-secret-key"})
146
147
  >>> jwk.sign(b"data", "HS256")
147
148
  >>> jwk.verify(token)
149
+ ```
148
150
  """
149
151
 
150
152
  _SUPPORTED_KEY_TYPES = frozenset({"RSA", "EC", "oct"})