jamlib 3.0.0a10__py3-none-any.whl

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 (93) hide show
  1. jam/__base__.py +451 -0
  2. jam/__base_encoder__.py +20 -0
  3. jam/__deprecated__.py +27 -0
  4. jam/__init__.py +17 -0
  5. jam/aio/__init__.py +10 -0
  6. jam/aio/instance.py +371 -0
  7. jam/aio/jwt/__init__.py +1 -0
  8. jam/aio/jwt/lists/__init__.py +1 -0
  9. jam/aio/jwt/lists/json.py +88 -0
  10. jam/aio/jwt/lists/redis.py +88 -0
  11. jam/aio/jwt/tools.py +88 -0
  12. jam/aio/oauth2/__init__.py +61 -0
  13. jam/aio/oauth2/builtin/__init__.py +3 -0
  14. jam/aio/oauth2/builtin/github.py +28 -0
  15. jam/aio/oauth2/builtin/gitlab.py +28 -0
  16. jam/aio/oauth2/builtin/google.py +28 -0
  17. jam/aio/oauth2/builtin/yandex.py +31 -0
  18. jam/aio/oauth2/client.py +151 -0
  19. jam/aio/sessions/__init__.py +87 -0
  20. jam/aio/sessions/json.py +85 -0
  21. jam/aio/sessions/redis.py +243 -0
  22. jam/encoders.py +47 -0
  23. jam/exceptions/__init__.py +19 -0
  24. jam/exceptions/jwt.py +39 -0
  25. jam/exceptions/oauth2.py +11 -0
  26. jam/exceptions/sessions.py +11 -0
  27. jam/ext/__init__.py +3 -0
  28. jam/ext/fastapi/__init__.py +11 -0
  29. jam/ext/flask/__init__.py +11 -0
  30. jam/ext/flask/extensions.py +161 -0
  31. jam/ext/litestar/__init__.py +13 -0
  32. jam/ext/litestar/middlewares.py +113 -0
  33. jam/ext/litestar/plugins.py +121 -0
  34. jam/ext/litestar/value.py +28 -0
  35. jam/ext/starlette/__init__.py +12 -0
  36. jam/ext/starlette/auth_backends.py +134 -0
  37. jam/ext/starlette/value.py +18 -0
  38. jam/instance.py +367 -0
  39. jam/jwt/__algorithms__.py +466 -0
  40. jam/jwt/__base__.py +38 -0
  41. jam/jwt/__init__.py +61 -0
  42. jam/jwt/__types__.py +15 -0
  43. jam/jwt/lists/__abc_list_repo__.py +27 -0
  44. jam/jwt/lists/__init__.py +11 -0
  45. jam/jwt/lists/json.py +110 -0
  46. jam/jwt/lists/redis.py +103 -0
  47. jam/jwt/module.py +228 -0
  48. jam/jwt/utils.py +32 -0
  49. jam/logger.py +76 -0
  50. jam/modules.py +29 -0
  51. jam/oauth2/__base__.py +113 -0
  52. jam/oauth2/__init__.py +69 -0
  53. jam/oauth2/builtin/__init__.py +3 -0
  54. jam/oauth2/builtin/github.py +28 -0
  55. jam/oauth2/builtin/gitlab.py +28 -0
  56. jam/oauth2/builtin/google.py +28 -0
  57. jam/oauth2/builtin/yandex.py +31 -0
  58. jam/oauth2/client.py +149 -0
  59. jam/otp/__base__.py +109 -0
  60. jam/otp/__init__.py +35 -0
  61. jam/otp/hotp.py +36 -0
  62. jam/otp/totp.py +73 -0
  63. jam/paseto/__base__.py +162 -0
  64. jam/paseto/__init__.py +57 -0
  65. jam/paseto/utils.py +99 -0
  66. jam/paseto/v1.py +314 -0
  67. jam/paseto/v2.py +235 -0
  68. jam/paseto/v3.py +361 -0
  69. jam/paseto/v4.py +276 -0
  70. jam/sessions/__base__.py +202 -0
  71. jam/sessions/__init__.py +89 -0
  72. jam/sessions/json.py +192 -0
  73. jam/sessions/redis.py +247 -0
  74. jam/tests/__init__.py +5 -0
  75. jam/tests/clients.py +914 -0
  76. jam/tests/fakers.py +39 -0
  77. jam/utils/__init__.py +35 -0
  78. jam/utils/aes.py +8 -0
  79. jam/utils/await_maybe.py +22 -0
  80. jam/utils/basic_auth.py +47 -0
  81. jam/utils/config_maker.py +236 -0
  82. jam/utils/ed.py +50 -0
  83. jam/utils/otp_keys.py +48 -0
  84. jam/utils/rsa.py +46 -0
  85. jam/utils/salt_hash.py +114 -0
  86. jam/utils/symmetric.py +17 -0
  87. jam/utils/xchacha20poly1305.py +61 -0
  88. jam/utils/xor.py +35 -0
  89. jamlib-3.0.0a10.dist-info/METADATA +117 -0
  90. jamlib-3.0.0a10.dist-info/RECORD +93 -0
  91. jamlib-3.0.0a10.dist-info/WHEEL +5 -0
  92. jamlib-3.0.0a10.dist-info/licenses/LICENSE.md +173 -0
  93. jamlib-3.0.0a10.dist-info/top_level.txt +1 -0
jam/aio/instance.py ADDED
@@ -0,0 +1,371 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from datetime import datetime
4
+ from this import d
5
+ from typing import Any, Optional, Union, Literal
6
+ import uuid
7
+
8
+ from jam.__base__ import BaseJam
9
+ from jam.logger import BaseLogger, JamLogger
10
+ from jam.encoders import BaseEncoder, JsonEncoder
11
+
12
+
13
+ class Jam(BaseJam):
14
+ """Main instance for aio."""
15
+
16
+ MODULES: dict[str, str] = {}
17
+
18
+ def __init__(
19
+ self,
20
+ config: Union[str, dict[str, Any]] = "pyproject.toml",
21
+ pointer: str = "jam",
22
+ *,
23
+ logger: type(BaseLogger) = JamLogger,
24
+ log_level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] = "INFO",
25
+ serializer: Union[BaseEncoder, type[BaseEncoder]] = JsonEncoder,
26
+ ) -> None:
27
+ super().__init__(config, pointer, logger, log_level, serializer)
28
+ self.__logger.warn("AIO VERSION NOT WORKING IN unstable VERSION")
29
+
30
+
31
+
32
+
33
+ async def jwt_make_payload(
34
+ self, exp: Optional[int], data: dict[str, Any]
35
+ ) -> dict[str, Any]:
36
+ """Make JWT-specific payload.
37
+
38
+ Args:
39
+ exp (int | None): Token expire, if None -> use default
40
+ data (dict[str, Any]): Data to payload
41
+
42
+ Returns:
43
+ dict[str, Any]: Payload
44
+ """
45
+ payload = {
46
+ "iat": datetime.now().timestamp(),
47
+ "exp": (datetime.now().timestamp() + exp) if exp else None,
48
+ "jti": str(uuid.uuid4()),
49
+ }
50
+ payload = payload | data
51
+ return payload
52
+
53
+ async def jwt_create_token(self, payload: dict[str, Any]) -> str:
54
+ """Create JWT token.
55
+
56
+ Args:
57
+ payload (dict[str, Any]): Data payload
58
+
59
+ Returns:
60
+ str: New token
61
+
62
+ Raises:
63
+ EmptySecretKey: If the HMAC algorithm is selected, but the secret key is None
64
+ EmtpyPrivateKey: If RSA algorithm is selected, but private key None
65
+ """
66
+ return self.jwt.encode(payload=payload)
67
+
68
+ async def jwt_verify_token(
69
+ self, token: str, check_exp: bool = True, check_list: bool = True
70
+ ) -> dict[str, Any]:
71
+ """Verify and decode JWT token.
72
+
73
+ Args:
74
+ token (str): JWT token
75
+ check_exp (bool): Check expire
76
+ check_list (bool): Check white/black list. Docs: https://jam.makridenko.ru/jwt/lists/what/
77
+
78
+ Returns:
79
+ dict[str, Any]: Decoded payload
80
+
81
+ Raises:
82
+ ValueError: If the token is invalid.
83
+ EmptySecretKey: If the HMAC algorithm is selected, but the secret key is None.
84
+ EmtpyPublicKey: If RSA algorithm is selected, but public key None.
85
+ NotFoundSomeInPayload: If 'exp' not found in payload.
86
+ TokenLifeTimeExpired: If token has expired.
87
+ TokenNotInWhiteList: If the list type is white, but the token is not there
88
+ TokenInBlackList: If the list type is black and the token is there
89
+ """
90
+ return self.jwt.decode(token)
91
+
92
+ async def session_create(
93
+ self, session_key: str, data: dict[str, Any]
94
+ ) -> str:
95
+ """Create new session.
96
+
97
+ Args:
98
+ session_key (str): Key for session
99
+ data (dict[str, Any]): Session data
100
+
101
+ Returns:
102
+ str: New session ID
103
+ """
104
+ return await self.session.create(session_key, data)
105
+
106
+ async def session_get(self, session_id: str) -> Optional[dict[str, Any]]:
107
+ """Get data from session.
108
+
109
+ Args:
110
+ session_id (str): Session ID
111
+
112
+ Returns:
113
+ dict[str, Any] | None: Session data if exist
114
+ """
115
+ return await self.session.get(session_id)
116
+
117
+ async def session_delete(self, session_id: str) -> None:
118
+ """Delete session.
119
+
120
+ Args:
121
+ session_id (str): Session ID
122
+ """
123
+ return await self.session.delete(session_id)
124
+
125
+ async def session_update(
126
+ self, session_id: str, data: dict[str, Any]
127
+ ) -> None:
128
+ """Update session data.
129
+
130
+ Args:
131
+ session_id (str): Session ID
132
+ data (dict[str, Any]): New data
133
+ """
134
+ return await self.session.update(session_id, data)
135
+
136
+ async def session_clear(self, session_key: str) -> None:
137
+ """Delete all sessions by key.
138
+
139
+ Args:
140
+ session_key (str): Key of session
141
+ """
142
+ return await self.session.clear(session_key)
143
+
144
+ async def session_rework(self, old_session_id: str) -> str:
145
+ """Rework session.
146
+
147
+ Args:
148
+ old_session_id (str): Old session id
149
+
150
+ Returns:
151
+ str: New session id
152
+ """
153
+ return await self.session.rework(old_session_id)
154
+
155
+ def otp_code(
156
+ self, secret: Union[str, bytes], factor: Optional[int] = None
157
+ ) -> str:
158
+ """Generates an OTP.
159
+
160
+ Args:
161
+ secret (str | bytes): User secret key.
162
+ factor (int | None, optional): Unixtime for TOTP(if none, use now time) / Counter for HOTP.
163
+
164
+ Returns:
165
+ str: OTP code (fixed-length string).
166
+ """
167
+ self._otp_checker()
168
+ return self._otp_module(
169
+ secret=secret, digits=self._otp.digits, digest=self._otp.digest
170
+ ).at(factor)
171
+
172
+ def otp_uri(
173
+ self,
174
+ secret: str,
175
+ name: Optional[str] = None,
176
+ issuer: Optional[str] = None,
177
+ counter: Optional[int] = None,
178
+ ) -> str:
179
+ """Generates an otpauth:// URI for Google Authenticator.
180
+
181
+ Args:
182
+ secret (str): User secret key.
183
+ name (str): Account name (e.g., email).
184
+ issuer (str): Service name (e.g., "GitHub").
185
+ counter (int | None, optional): Counter (for HOTP). Default is None.
186
+
187
+ Returns:
188
+ str: A string of the form "otpauth://..."
189
+ """
190
+ self._otp_checker()
191
+ return self._otp_module(
192
+ secret=secret, digits=self._otp.digits, digest=self._otp.digest
193
+ ).provisioning_uri(
194
+ name=name, issuer=issuer, type_=self._otp.type, counter=counter
195
+ )
196
+
197
+ def otp_verify_code(
198
+ self,
199
+ secret: Union[str, bytes],
200
+ code: str,
201
+ factor: Optional[int] = None,
202
+ look_ahead: Optional[int] = 1,
203
+ ) -> bool:
204
+ """Checks the OTP code, taking into account the acceptable window.
205
+
206
+ Args:
207
+ secret (str | bytes): User secret key.
208
+ code (str): The code entered.
209
+ factor (int | None, optional): Unixtime for TOTP(if none, use now time) / Counter for HOTP.
210
+ look_ahead (int, optional): Acceptable deviation in intervals (±window(totp) / ±look ahead(hotp)). Default is 1.
211
+
212
+ Returns:
213
+ bool: True if the code matches, otherwise False.
214
+ """
215
+ self._otp_checker()
216
+ return self._otp_module(
217
+ secret=secret, digits=self._otp.digits, digest=self._otp.digest
218
+ ).verify(code=code, factor=factor, look_ahead=look_ahead)
219
+
220
+ async def oauth2_get_authorized_url(
221
+ self, provider: str, scope: list[str], **extra_params: Any
222
+ ) -> str:
223
+ """Generate full OAuth2 authorization URL.
224
+
225
+ Args:
226
+ provider (str): Provider name
227
+ scope (list[str]): Auth scope
228
+ extra_params (Any): Extra ath params
229
+
230
+ Returns:
231
+ str: Authorization url
232
+ """
233
+ from jam.exceptions import ProviderNotConfigurError
234
+
235
+ if provider not in self.oauth2:
236
+ raise ProviderNotConfigurError(
237
+ f"Provider {provider} not configured"
238
+ )
239
+ return await self.oauth2[provider].get_authorization_url(
240
+ scope, **extra_params
241
+ )
242
+
243
+ async def oauth2_fetch_token(
244
+ self,
245
+ provider: str,
246
+ code: str,
247
+ grant_type: str = "authorization_code",
248
+ **extra_params: Any,
249
+ ) -> dict[str, Any]:
250
+ """Exchange authorization code for access token.
251
+
252
+ Args:
253
+ provider (str): Provider name
254
+ code (str): OAuth2 code
255
+ grant_type (str): Type of oauth2 grant
256
+ extra_params (Any): Extra auth params if needed
257
+
258
+ Returns:
259
+ dict: OAuth2 token
260
+ """
261
+ from jam.exceptions import ProviderNotConfigurError
262
+
263
+ if provider not in self.oauth2:
264
+ raise ProviderNotConfigurError(
265
+ f"Provider {provider} not configured"
266
+ )
267
+ return await self.oauth2[provider].fetch_token(
268
+ code, grant_type, **extra_params
269
+ )
270
+
271
+ async def oauth2_refresh_token(
272
+ self,
273
+ provider: str,
274
+ refresh_token: str,
275
+ grant_type: str = "refresh_token",
276
+ **extra_params: Any,
277
+ ) -> dict[str, Any]:
278
+ """Use refresh token to obtain a new access token.
279
+
280
+ Args:
281
+ provider (str): Provider name
282
+ refresh_token (str): Refresh token
283
+ grant_type (str): Grant type
284
+ extra_params (Any): Extra auth params if needed
285
+
286
+ Returns:
287
+ dict: Refresh token
288
+ """
289
+ from jam.exceptions import ProviderNotConfigurError
290
+
291
+ if provider not in self.oauth2:
292
+ raise ProviderNotConfigurError(
293
+ f"Provider {provider} not configured"
294
+ )
295
+ return await self.oauth2[provider].refresh_token(
296
+ refresh_token, grant_type, **extra_params
297
+ )
298
+
299
+ async def oauth2_client_credentials_flow(
300
+ self,
301
+ provider: str,
302
+ scope: Optional[list[str]] = None,
303
+ **extra_params: Any,
304
+ ) -> dict[str, Any]:
305
+ """Obtain access token using client credentials flow (no user interaction).
306
+
307
+ Args:
308
+ provider (str): Provider name
309
+ scope (list[str] | None): Auth scope
310
+ extra_params (Any): Extra auth params if needed
311
+
312
+ Returns:
313
+ dict: JSON with access token
314
+ """
315
+ from jam.exceptions import ProviderNotConfigurError
316
+
317
+ if provider not in self.oauth2:
318
+ raise ProviderNotConfigurError(
319
+ f"Provider {provider} not configured"
320
+ )
321
+ return await self.oauth2[provider].client_credentials_flow(
322
+ scope, **extra_params
323
+ )
324
+
325
+ async def paseto_make_payload(
326
+ self, exp: Optional[int] = None, **data: dict[str, Any]
327
+ ) -> dict[str, Any]:
328
+ """Generate payload for PASETO.
329
+
330
+ Args:
331
+ exp (int | None): Token expire
332
+ data (dict[str, Any]): Custom data
333
+
334
+ Returns:
335
+ dict: Payload
336
+ """
337
+ from jam.paseto.utils import payload_maker
338
+
339
+ return payload_maker(expire=exp, data=data)
340
+
341
+ async def paseto_create(
342
+ self,
343
+ payload: dict[str, Any],
344
+ footer: Optional[Union[dict[str, Any], str]],
345
+ ) -> str:
346
+ """Create new PASETO.
347
+
348
+ Args:
349
+ payload (dict[str, Any]): Payload
350
+ footer (dict | str | None): Footer
351
+
352
+ Returns:
353
+ str: New token
354
+ """
355
+ return self.paseto.encode(payload=payload, footer=footer)
356
+
357
+ async def paseto_decode(
358
+ self, token: str, check_exp: bool = True, check_list: bool = True
359
+ ) -> dict[str, Union[dict, Union[str, dict, None]]]:
360
+ """Decode PASETO.
361
+
362
+ Args:
363
+ token (str): Token
364
+ check_exp (bool): Check exp in payload
365
+ check_list (bool): Check token in list
366
+
367
+ Returns:
368
+ dict: {'payload' PAYLOAD, 'footer': FOOTER}
369
+ """
370
+ payload, footer = self.paseto.decode(token)
371
+ return {"payload": payload, "footer": footer}
@@ -0,0 +1 @@
1
+ # -*- coding: utf-8 -*-
@@ -0,0 +1 @@
1
+ # -*- coding: utf-8 -*-
@@ -0,0 +1,88 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from asyncio import to_thread
4
+ import datetime
5
+ from typing import Literal
6
+
7
+ from jam.jwt.lists.__abc_list_repo__ import BaseJWTList
8
+
9
+
10
+ try:
11
+ from tinydb import Query, TinyDB
12
+ except ImportError:
13
+ raise ImportError(
14
+ """
15
+ No required packages found, looks like you didn't install them:
16
+ `pip install "jamlib[json]"`
17
+ """
18
+ )
19
+
20
+
21
+ class JSONList(BaseJWTList):
22
+ """Black/White list in JSON format, not recommended for blacklists because it is not convenient to control token lifetime.
23
+
24
+ Dependency required:
25
+ `pip install jamlib[json]`
26
+
27
+ Attributes:
28
+ __list__ (TinyDB): TinyDB instance
29
+
30
+ Methods:
31
+ add: adding token to list
32
+ check: check token in list
33
+ delete: removing token from list
34
+ """
35
+
36
+ def __init__(
37
+ self, type: Literal["white", "black"], json_path: str = "whitelist.json"
38
+ ) -> None:
39
+ """Class constructor.
40
+
41
+ Args:
42
+ type (Literal["white", "black"]): Type of list
43
+ json_path (str): Path to .json file
44
+ """
45
+ super().__init__(list_type=type)
46
+ self.__list__ = TinyDB(json_path)
47
+
48
+ async def add(self, token: str) -> None:
49
+ """Method for adding token to list.
50
+
51
+ Args:
52
+ token (str): Your JWT token
53
+
54
+ Returns:
55
+ (None)
56
+ """
57
+ _doc = {
58
+ "token": token,
59
+ "timestamp": datetime.datetime.now().timestamp(),
60
+ }
61
+
62
+ await to_thread(self.__list__.insert, _doc)
63
+ return None
64
+
65
+ async def check(self, token: str) -> bool:
66
+ """Method for checking if a token is present in list.
67
+
68
+ Args:
69
+ token (str): Your jwt token
70
+
71
+ Returns:
72
+ (bool)
73
+ """
74
+ cond = Query()
75
+ _token = await to_thread(self.__list__.search, cond.token == token)
76
+ return bool(_token)
77
+
78
+ async def delete(self, token: str) -> None:
79
+ """Method for removing token from list.
80
+
81
+ Args:
82
+ token (str): Your jwt token
83
+
84
+ Returns:
85
+ (None)
86
+ """
87
+ cond = Query()
88
+ await to_thread(self.__list__.remove, cond.token == token)
@@ -0,0 +1,88 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ import datetime
4
+ from typing import Literal, Optional, Union
5
+
6
+ from jam.jwt.lists.__abc_list_repo__ import BaseJWTList
7
+
8
+
9
+ try:
10
+ from redis.asyncio import Redis
11
+ except ImportError:
12
+ raise ImportError(
13
+ """
14
+ No required packages found, looks like you didn't install them:
15
+ `pip install "jamlib[redis]"`
16
+ """
17
+ )
18
+
19
+
20
+ class RedisList(BaseJWTList):
21
+ """Black/White lists in Redis, most optimal format.
22
+
23
+ Dependency required: `pip install jamlib[redis]`
24
+
25
+ Attributes:
26
+ __list__ (Redis): Redis instance
27
+ exp (int | None): Token lifetime
28
+ """
29
+
30
+ def __init__(
31
+ self,
32
+ type: Literal["white", "black"],
33
+ redis_uri: Union[str, Redis],
34
+ in_list_life_time: Optional[int] = None,
35
+ ) -> None:
36
+ """Class constructor.
37
+
38
+ Args:
39
+ type (Literal["white", "black"]): Type of list
40
+ redis_uri (str): Uri to redis connect
41
+ in_list_life_time (int | None): The lifetime of a token in the list
42
+ """
43
+ super().__init__(list_type=type)
44
+ if isinstance(redis_uri, str):
45
+ self.__list__ = Redis.from_url(redis_uri, decode_responses=True)
46
+ else:
47
+ self.__list__ = redis_uri
48
+ self.exp = in_list_life_time
49
+
50
+ async def add(self, token: str) -> None:
51
+ """Method for adding token to list.
52
+
53
+ Args:
54
+ token (str): Your JWT token
55
+
56
+ Returns:
57
+ (None)
58
+ """
59
+ await self.__list__.set(
60
+ name=token, value=str(datetime.datetime.now()), ex=self.exp
61
+ )
62
+ return None
63
+
64
+ async def check(self, token: str) -> bool:
65
+ """Method for checking if a token is present in the list.
66
+
67
+ Args:
68
+ token (str): Your JWT token
69
+
70
+ Returns:
71
+ (bool)
72
+ """
73
+ _token = await self.__list__.get(name=token)
74
+ if _token:
75
+ return True
76
+ return False
77
+
78
+ async def delete(self, token: str) -> None:
79
+ """Method for removing a token from a list.
80
+
81
+ Args:
82
+ token (str): Your JWT token
83
+
84
+ Returns:
85
+ None
86
+ """
87
+ await self.__list__.delete(token)
88
+ return None
jam/aio/jwt/tools.py ADDED
@@ -0,0 +1,88 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from asyncio import to_thread
4
+ from typing import Any, Optional
5
+
6
+ from jam.jwt.tools import __gen_jwt__, __payload_maker__, __validate_jwt__
7
+
8
+
9
+ async def __gen_jwt_async__(
10
+ header: dict[str, Any],
11
+ payload: dict[str, Any],
12
+ secret: Optional[str] = None,
13
+ private_key: Optional[str] = None,
14
+ ) -> str:
15
+ """Method for generating JWT token with different algorithms.
16
+
17
+ Example:
18
+ ```python
19
+ token = await __gen_jwt_async__(
20
+ header={
21
+ "alg": "HS256",
22
+ "type": "jwt"
23
+ },
24
+ payload={
25
+ "id": 1
26
+ },
27
+ secret="SUPER_SECRET"
28
+ )
29
+ ```
30
+
31
+ Args:
32
+ header (dict[str, str]): Dict with JWT headers
33
+ payload (dict[str, Any]): Custom JWT payload
34
+ secret (str | None): Secret key for HMAC algorithms
35
+ private_key (str | None): Private key for RSA algorithms
36
+
37
+ Raises:
38
+ EmptySecretKey: If the HMAC algorithm is selected, but the secret key is None
39
+ EmtpyPrivateKey: If RSA algorithm is selected, but private key None
40
+
41
+ Returns:
42
+ (str): Access/refresh token
43
+ """
44
+ return await to_thread(__gen_jwt__, header, payload, secret, private_key)
45
+
46
+
47
+ async def __validate_jwt_async__(
48
+ token: str,
49
+ check_exp: bool = False,
50
+ secret: Optional[str] = None,
51
+ public_key: Optional[str] = None,
52
+ ) -> dict[str, Any]:
53
+ """Validate a JWT token and return the payload if valid.
54
+
55
+ Args:
56
+ token (str): The JWT token to validate.
57
+ check_exp (bool): true to check token lifetime.
58
+ secret (str | None): Secret key for HMAC algorithms.
59
+ public_key (str | None): Public key for RSA algorithms.
60
+
61
+ Returns:
62
+ (dict[str, Any]): The payload if the token is valid.
63
+
64
+ Raises:
65
+ ValueError: If the token is invalid.
66
+ EmptySecretKey: If the HMAC algorithm is selected, but the secret key is None.
67
+ EmtpyPublicKey: If RSA algorithm is selected, but public key None.
68
+ NotFoundSomeInPayload: If 'exp' not found in payload.
69
+ TokenLifeTimeExpired: If token has expired.
70
+ """
71
+ return await to_thread(
72
+ __validate_jwt__, token, check_exp, secret, public_key
73
+ )
74
+
75
+
76
+ async def __payload_maker_async__(
77
+ exp: Optional[int] = None, **data: Any
78
+ ) -> dict[str, Any]:
79
+ """Tool for making base payload.
80
+
81
+ Args:
82
+ exp (int | None): Token expire
83
+ **data: Data for payload
84
+
85
+ Returns:
86
+ (dict[str, Any])
87
+ """
88
+ return await to_thread(__payload_maker__, exp, **data)
@@ -0,0 +1,61 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ """Async OAuth2 modules."""
4
+
5
+ from typing import Any
6
+
7
+ from .builtin.github import GitHubOAuth2Client
8
+ from .builtin.gitlab import GitLabOAuth2Client
9
+ from .builtin.google import GoogleOAuth2Client
10
+ from .builtin.yandex import YandexOAuth2Client
11
+ from jam.oauth2.__abc_oauth2_repo__ import BaseOAuth2Client
12
+ from jam.logger import BaseLogger, logger
13
+
14
+
15
+ BUILTIN_PROVIDERS = {
16
+ "github": "jam.aio.oauth2.builtin.github.GitHubOAuth2Client",
17
+ "gitlab": "jam.aio.oauth2.builtin.gitlab.GitLabOAuth2Client",
18
+ "google": "jam.aio.oauth2.builtin.google.GoogleOAuth2Client",
19
+ "yandex": "jam.aio.oauth2.builtin.yandex.YandexOAuth2Client",
20
+ }
21
+
22
+
23
+ def create_instance(
24
+ providers: dict[str, dict],
25
+ logger: BaseLogger = logger,
26
+ **kwargs: Any
27
+ ) -> dict[str, BaseOAuth2Client]:
28
+ """Create async OAuth2 clients for configured providers.
29
+
30
+ Args:
31
+ providers: {provider_name: {client_id, client_secret, redirect_uri, ...}}
32
+ logger: Logger instance
33
+ **kwargs: Additional params
34
+
35
+ Returns:
36
+ dict: {provider_name: OAuth2Client instance}
37
+ """
38
+ from jam.utils.config_maker import __module_loader__
39
+
40
+ result = {}
41
+ for name, cfg in providers.items():
42
+ cfg = cfg.copy() # Don't modify original config
43
+
44
+ if "custom_module" in cfg:
45
+ module_cls = __module_loader__(cfg.pop("custom_module"))
46
+ else:
47
+ module_path = BUILTIN_PROVIDERS.get(name, "jam.aio.oauth2.client.OAuth2Client")
48
+ module_cls = __module_loader__(module_path)
49
+
50
+ result[name] = module_cls(**cfg)
51
+
52
+ return result
53
+
54
+
55
+ __all__ = [
56
+ "GitLabOAuth2Client",
57
+ "GitHubOAuth2Client",
58
+ "GoogleOAuth2Client",
59
+ "YandexOAuth2Client",
60
+ "create_instance",
61
+ ]
@@ -0,0 +1,3 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ """Built-in OAuth2 providers."""