jamlib 3.0.0b13.dev2__tar.gz → 3.0.0b15__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 (106) hide show
  1. {jamlib-3.0.0b13.dev2/src/jamlib.egg-info → jamlib-3.0.0b15}/PKG-INFO +1 -1
  2. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/pyproject.toml +1 -1
  3. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/__init__.py +1 -1
  4. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/exceptions/__init__.py +7 -11
  5. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/exceptions/plugins.py +9 -8
  6. jamlib-3.0.0b15/src/jam/ext/flask/__init__.py +26 -0
  7. jamlib-3.0.0b15/src/jam/ext/flask/extensions.py +212 -0
  8. jamlib-3.0.0b15/src/jam/ext/flask/objects.py +12 -0
  9. jamlib-3.0.0b15/src/jam/ext/starlette/__init__.py +12 -0
  10. jamlib-3.0.0b15/src/jam/ext/starlette/auth_backends.py +143 -0
  11. jamlib-3.0.0b15/src/jam/ext/starlette/value.py +18 -0
  12. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15/src/jamlib.egg-info}/PKG-INFO +1 -1
  13. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jamlib.egg-info/SOURCES.txt +3 -2
  14. jamlib-3.0.0b13.dev2/src/jam/ext/flask/__init__.py +0 -11
  15. jamlib-3.0.0b13.dev2/src/jam/ext/flask/extensions.py +0 -167
  16. jamlib-3.0.0b13.dev2/src/jam/ext/starlette/__init__.py +0 -17
  17. jamlib-3.0.0b13.dev2/src/jam/ext/starlette/backends.py +0 -132
  18. jamlib-3.0.0b13.dev2/src/jam/ext/starlette/objects.py +0 -53
  19. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/LICENSE.md +0 -0
  20. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/README.md +0 -0
  21. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/setup.cfg +0 -0
  22. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/__base__.py +0 -0
  23. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/__base_encoder__.py +0 -0
  24. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/__deprecated__.py +0 -0
  25. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/aio/__base__.py +0 -0
  26. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/aio/__init__.py +0 -0
  27. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/aio/instance.py +0 -0
  28. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/aio/jwt/__init__.py +0 -0
  29. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/aio/oauth2/__base__.py +0 -0
  30. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/aio/oauth2/__init__.py +0 -0
  31. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/aio/oauth2/builtin/__init__.py +0 -0
  32. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/aio/oauth2/builtin/github.py +0 -0
  33. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/aio/oauth2/builtin/gitlab.py +0 -0
  34. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/aio/oauth2/builtin/google.py +0 -0
  35. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/aio/oauth2/builtin/yandex.py +0 -0
  36. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/aio/oauth2/client.py +0 -0
  37. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/aio/sessions/__base__.py +0 -0
  38. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/aio/sessions/__init__.py +0 -0
  39. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/aio/sessions/json.py +0 -0
  40. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/aio/sessions/redis.py +0 -0
  41. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/encoders.py +0 -0
  42. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/exceptions/base.py +0 -0
  43. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/exceptions/jwt.py +0 -0
  44. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/exceptions/oauth2.py +0 -0
  45. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/exceptions/paseto.py +0 -0
  46. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/exceptions/sessions.py +0 -0
  47. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/ext/__init__.py +0 -0
  48. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/ext/fastapi/__init__.py +0 -0
  49. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/ext/litestar/__init__.py +0 -0
  50. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/ext/litestar/middleware.py +0 -0
  51. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/ext/litestar/objects.py +0 -0
  52. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/ext/litestar/plugins.py +0 -0
  53. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/instance.py +0 -0
  54. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/jwt/__algorithms__.py +0 -0
  55. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/jwt/__base__.py +0 -0
  56. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/jwt/__init__.py +0 -0
  57. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/jwt/__types__.py +0 -0
  58. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/jwt/lists/__abc_list_repo__.py +0 -0
  59. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/jwt/lists/__init__.py +0 -0
  60. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/jwt/lists/json.py +0 -0
  61. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/jwt/lists/redis.py +0 -0
  62. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/jwt/module.py +0 -0
  63. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/jwt/utils.py +0 -0
  64. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/logger.py +0 -0
  65. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/oauth2/__base__.py +0 -0
  66. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/oauth2/__init__.py +0 -0
  67. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/oauth2/builtin/__init__.py +0 -0
  68. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/oauth2/builtin/github.py +0 -0
  69. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/oauth2/builtin/gitlab.py +0 -0
  70. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/oauth2/builtin/google.py +0 -0
  71. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/oauth2/builtin/yandex.py +0 -0
  72. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/oauth2/client.py +0 -0
  73. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/otp/__base__.py +0 -0
  74. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/otp/__init__.py +0 -0
  75. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/otp/hotp.py +0 -0
  76. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/otp/totp.py +0 -0
  77. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/paseto/__base__.py +0 -0
  78. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/paseto/__init__.py +0 -0
  79. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/paseto/utils.py +0 -0
  80. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/paseto/v1.py +0 -0
  81. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/paseto/v2.py +0 -0
  82. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/paseto/v3.py +0 -0
  83. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/paseto/v4.py +0 -0
  84. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/py.typed +0 -0
  85. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/sessions/__base__.py +0 -0
  86. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/sessions/__init__.py +0 -0
  87. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/sessions/json.py +0 -0
  88. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/sessions/redis.py +0 -0
  89. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/tests/__init__.py +0 -0
  90. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/tests/clients.py +0 -0
  91. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/tests/fakers.py +0 -0
  92. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/utils/__init__.py +0 -0
  93. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/utils/aes.py +0 -0
  94. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/utils/await_maybe.py +0 -0
  95. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/utils/basic_auth.py +0 -0
  96. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/utils/config_maker.py +0 -0
  97. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/utils/ed.py +0 -0
  98. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/utils/otp_keys.py +0 -0
  99. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/utils/rsa.py +0 -0
  100. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/utils/salt_hash.py +0 -0
  101. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/utils/symmetric.py +0 -0
  102. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/utils/xchacha20poly1305.py +0 -0
  103. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/utils/xor.py +0 -0
  104. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jamlib.egg-info/dependency_links.txt +0 -0
  105. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jamlib.egg-info/requires.txt +0 -0
  106. {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/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.0.0b13.dev2
3
+ Version: 3.0.0b15
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.0.0b13.dev2"
3
+ version = "3.0.0b15"
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.0.0b13.dev2"
15
+ __version__ = "3.0.0b15"
16
16
  __all__ = ["Jam", "JsonEncoder", "BaseJam", "BaseEncoder"]
@@ -4,16 +4,12 @@
4
4
  All Jam exceptions
5
5
  """
6
6
 
7
- from .base import (
8
- JamError,
9
- JamConfigurationError,
10
- JamValidationError
11
- )
7
+ from .base import JamError, JamConfigurationError, JamValidationError
12
8
 
13
9
  from .oauth2 import (
14
10
  JamOAuth2Error,
15
11
  JamOAuth2EmptyRaw,
16
- JamOAuth2ProviderNotConfigured
12
+ JamOAuth2ProviderNotConfigured,
17
13
  )
18
14
 
19
15
  from .jwt import (
@@ -33,14 +29,14 @@ from .paseto import (
33
29
  JamPASETOInvalidSecp384r1Key,
34
30
  JamPASTOKeyVerificationError,
35
31
  JamPASETOInvalidPurpose,
36
- JamPASETOInvalidTokenFormat
32
+ JamPASETOInvalidTokenFormat,
37
33
  )
38
34
 
39
35
  from .plugins import (
36
+ JamFlaskPluginConfigError,
37
+ JamFlaskPluginError,
40
38
  JamLitestarPluginConfigError,
41
39
  JamLitestarPluginError,
42
- JamStarlettePluginConfigError,
43
- JamStarlettePluginError
44
40
  )
45
41
 
46
42
  from .sessions import (
@@ -72,8 +68,8 @@ __all__ = [
72
68
  "JamPASTOKeyVerificationError",
73
69
  "JamLitestarPluginConfigError",
74
70
  "JamLitestarPluginError",
75
- "JamStarlettePluginConfigError",
76
- "JamStarlettePluginError",
71
+ "JamFlaskPluginConfigError",
72
+ "JamFlaskPluginError",
77
73
  "JamSessionNotFound",
78
74
  "JamSessionEmptyAESKey",
79
75
  ]
@@ -16,15 +16,16 @@ class JamLitestarPluginError(JamError):
16
16
  default_message = "An error occurred for a litestar plugin."
17
17
  default_code = "jam.plugin.litestar"
18
18
 
19
- class JamStarlettePluginConfigError(JamConfigurationError):
20
- """Exception raised when a configuration error occurs for a starlette plugin."""
21
19
 
22
- default_message = "Configuration error occurred for a starlette plugin."
23
- default_code = "jam.configuration.plugin.starlette"
20
+ class JamFlaskPluginConfigError(JamConfigurationError):
21
+ """Exception raised when a configuration error occurs for a flask plugin."""
24
22
 
23
+ default_message = "Configuration error occurred for a flask plugin."
24
+ default_code = "jam.configuration.plugin.flask"
25
25
 
26
- class JamStarlettePluginError(JamError):
27
- """Exception raised when an error occurs for a starlette plugin."""
28
26
 
29
- default_message = "An error occurred for a starlette plugin."
30
- default_code = "jam.plugin.starlette"
27
+ class JamFlaskPluginError(JamError):
28
+ """Exception raised when an error occurs for a flask plugin."""
29
+
30
+ default_message = "An error occurred for a flask plugin."
31
+ default_code = "jam.plugin.flask"
@@ -0,0 +1,26 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ """Flask integration.
4
+
5
+ Flask docs: https://flask.palletsprojects.com
6
+ """
7
+
8
+ from .extensions import (
9
+ JWTExtension,
10
+ OAuth2Extension,
11
+ PASETOExtension,
12
+ SessionExtension,
13
+ )
14
+
15
+ from .objects import (
16
+ Token,
17
+ )
18
+
19
+
20
+ __all__ = [
21
+ "Token",
22
+ "JWTExtension",
23
+ "SessionExtension",
24
+ "PASETOExtension",
25
+ "OAuth2Extension",
26
+ ]
@@ -0,0 +1,212 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from typing import Any
4
+
5
+ import flask
6
+
7
+ from jam.exceptions import JamFlaskPluginConfigError
8
+ from jam.jwt import JWT
9
+ from jam.oauth2 import create_instance as create_oauth2
10
+ from jam.paseto import create_instance as create_paseto
11
+ from jam.sessions import create_instance as create_session
12
+ from jam.utils.config_maker import GENERIC_POINTER, __config_maker__
13
+
14
+
15
+ class BaseExtension:
16
+ """Base Jam extension for Flask."""
17
+
18
+ MODULE: Any
19
+ _CONFIG_KEY: str
20
+
21
+ def __init__(
22
+ self,
23
+ app: flask.Flask | None = None,
24
+ auth: Any | None = None,
25
+ **kwargs: Any,
26
+ ) -> None:
27
+ """Initialize the extension.
28
+
29
+ Args:
30
+ app (flask.Flask | None): Flask application instance
31
+ auth (Any | None): Pre-created auth module instance
32
+ **kwargs: Configuration arguments
33
+ """
34
+ self.app = app
35
+ self._auth = auth
36
+ self._config = kwargs
37
+ if app is not None:
38
+ self.init_app(app)
39
+
40
+ def init_app(self, app: flask.Flask) -> None:
41
+ """Initialize the Flask application.
42
+
43
+ Args:
44
+ app (flask.Flask): Flask application instance
45
+ """
46
+ self.app = app
47
+ if self._auth is None:
48
+ self._auth = self.MODULE(**self._config)
49
+ app.extensions[self._CONFIG_KEY] = self._auth
50
+
51
+
52
+ class BaseAuthExtension(BaseExtension):
53
+ """Base Jam authentication extension for Flask."""
54
+
55
+ MODULE: Any
56
+ _CONFIG_KEY: str
57
+
58
+ def __init__(
59
+ self,
60
+ app: flask.Flask | None = None,
61
+ auth: Any | None = None,
62
+ config: dict[str, Any] | str | None = None,
63
+ pointer: str = GENERIC_POINTER,
64
+ cookie_name: str | None = None,
65
+ header_name: str | None = None,
66
+ bearer: bool = True,
67
+ **kwargs: Any,
68
+ ) -> None:
69
+ """Initialize the authentication extension.
70
+
71
+ Args:
72
+ app (flask.Flask | None): Flask application instance
73
+ auth (Any | None): Pre-created auth module instance
74
+ config (dict[str, Any] | str | None): Jam config as path/to/file or dict.
75
+ pointer (str): Config pointer
76
+ cookie_name (str | None): Cookie name to read token
77
+ header_name (str | None): Header name to read token
78
+ bearer (bool): Strip "Bearer " prefix from header
79
+ **kwargs: Configuration arguments if config=None
80
+ """
81
+ self._cookie_name = cookie_name
82
+ self._header_name = header_name
83
+ self._bearer = bearer
84
+
85
+ if not cookie_name and not header_name:
86
+ raise JamFlaskPluginConfigError(
87
+ message="Cookie name and header name cannot be both None.",
88
+ details={
89
+ "cookie_name": cookie_name,
90
+ "header_name": header_name,
91
+ },
92
+ )
93
+
94
+ _config: dict[str, Any] | None = (
95
+ __config_maker__(config, pointer) if config else None
96
+ )
97
+
98
+ params = _config.pop(self._CONFIG_KEY) if _config else kwargs
99
+ super().__init__(app, auth=auth, **params)
100
+
101
+ def _get_token(self) -> str | None:
102
+ token = None
103
+ if self._header_name:
104
+ token = flask.request.headers.get(self._header_name, None)
105
+ if token and self._bearer and token.startswith("Bearer "):
106
+ token = token[7:]
107
+ elif self._cookie_name:
108
+ token = flask.request.cookies.get(self._cookie_name, None)
109
+ return token
110
+
111
+ def _get_payload(self) -> dict[str, Any] | None:
112
+ raise NotImplementedError
113
+
114
+ def init_app(self, app: flask.Flask) -> None:
115
+ """Initialize the Flask application."""
116
+ super().init_app(app)
117
+ app.before_request(self._put_auth_in_g)
118
+
119
+ def _put_auth_in_g(self) -> None:
120
+ setattr(flask.g, self._CONFIG_KEY, self._auth)
121
+ self._get_payload()
122
+
123
+
124
+ class JWTExtension(BaseAuthExtension):
125
+ """JWT extension for Flask."""
126
+
127
+ MODULE = staticmethod(JWT)
128
+ _CONFIG_KEY = "jwt"
129
+
130
+ def _get_payload(self) -> dict[str, Any] | None:
131
+ token = self._get_token()
132
+ flask.g.payload = None
133
+ if not token:
134
+ return None
135
+ try:
136
+ payload = self._auth.decode(token)
137
+ flask.g.payload = payload
138
+ return payload
139
+ except Exception:
140
+ return None
141
+
142
+
143
+ class SessionExtension(BaseAuthExtension):
144
+ """Session extension for Flask."""
145
+
146
+ MODULE = staticmethod(create_session)
147
+ _CONFIG_KEY = "sessions"
148
+
149
+ def _get_payload(self) -> dict[str, Any] | None:
150
+ token = self._get_token()
151
+ flask.g.payload = None
152
+ if not token:
153
+ return None
154
+ try:
155
+ payload = self._auth.get(token)
156
+ flask.g.payload = payload
157
+ return payload
158
+ except Exception:
159
+ return None
160
+
161
+
162
+ class PASETOExtension(BaseAuthExtension):
163
+ """PASETO extension for Flask."""
164
+
165
+ MODULE = staticmethod(create_paseto)
166
+ _CONFIG_KEY = "paseto"
167
+
168
+ def _get_payload(self) -> dict[str, Any] | None:
169
+ token = self._get_token()
170
+ flask.g.payload = None
171
+ if not token:
172
+ return None
173
+ try:
174
+ data = self._auth.decode(token)
175
+ if not data:
176
+ return None
177
+ payload = data[0] if isinstance(data, tuple) else data
178
+ flask.g.payload = payload
179
+ return payload
180
+ except Exception:
181
+ return None
182
+
183
+
184
+ class OAuth2Extension(BaseExtension):
185
+ """OAuth2 extension for Flask."""
186
+
187
+ MODULE = staticmethod(create_oauth2)
188
+ _CONFIG_KEY = "oauth2"
189
+
190
+ def __init__(
191
+ self,
192
+ app: flask.Flask | None = None,
193
+ auth: Any | None = None,
194
+ config: dict[str, Any] | str | None = None,
195
+ pointer: str = GENERIC_POINTER,
196
+ **kwargs: Any,
197
+ ) -> None:
198
+ """Initialize the OAuth2 extension.
199
+
200
+ Args:
201
+ app (flask.Flask | None): Flask application instance
202
+ auth (Any | None): Pre-created auth module instance
203
+ config (dict[str, Any] | str | None): Jam config as path/to/file or dict.
204
+ pointer (str): Config pointer
205
+ **kwargs: Configuration arguments if config=None
206
+ """
207
+ _config: dict[str, Any] | None = (
208
+ __config_maker__(config, pointer) if config else None
209
+ )
210
+
211
+ params = _config.pop(self._CONFIG_KEY) if _config else kwargs
212
+ super().__init__(app, auth=auth, **params)
@@ -0,0 +1,12 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+
8
+ @dataclass(slots=True)
9
+ class Token:
10
+ """Data for Authresult."""
11
+
12
+ token: str | None
@@ -0,0 +1,12 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ """
4
+ Starlette integration.
5
+
6
+ Starlette docs: https://starlette.dev
7
+ """
8
+
9
+ from .auth_backends import JWTBackend, SessionBackend
10
+
11
+
12
+ __all__ = ["JWTBackend", "SessionBackend"]
@@ -0,0 +1,143 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from typing import Any
4
+
5
+ from starlette.authentication import (
6
+ AuthCredentials,
7
+ AuthenticationBackend,
8
+ AuthenticationError,
9
+ BaseUser,
10
+ )
11
+ from starlette.requests import HTTPConnection
12
+
13
+ from jam.__base__ import BaseJam
14
+ from jam.utils.await_maybe import await_maybe
15
+
16
+ from .value import Payload
17
+
18
+
19
+ class JWTBackend(AuthenticationBackend):
20
+ """JWT Backend for Starlette AuthenticationMiddleware."""
21
+
22
+ def __init__(
23
+ self,
24
+ jam: BaseJam,
25
+ cookie_name: str | None = None,
26
+ header_name: str | None = "Authorization",
27
+ ) -> None:
28
+ """Constructor.
29
+
30
+ Args:
31
+ jam (BaseJam): Jam instance
32
+ cookie_name (str | None): Access token cookie name
33
+ header_name (str | None): Access token header name
34
+ """
35
+ self._jam = jam
36
+ self.cookie_name = cookie_name
37
+ self.header_name = header_name
38
+ self.__use_list = bool(getattr(self._jam.jwt, "list", False))
39
+
40
+ async def authenticate(
41
+ self, conn: HTTPConnection
42
+ ) -> tuple[AuthCredentials, BaseUser] | None:
43
+ """Starlette authentication handler."""
44
+ logger = self._jam._logger
45
+ token = None
46
+
47
+ logger.debug("JWTBackend: Attempting to extract token from request")
48
+ if self.cookie_name:
49
+ token = conn.cookies.get(self.cookie_name)
50
+ if token:
51
+ logger.debug(f"Token found in cookie '{self.cookie_name}'")
52
+
53
+ if not token and self.header_name:
54
+ header = conn.headers.get(self.header_name)
55
+ if header and header.startswith("Bearer "):
56
+ token = header.split("Bearer ")[1]
57
+ logger.debug(f"Token found in header '{self.header_name}'")
58
+
59
+ if not token:
60
+ logger.debug("No token found in request")
61
+ return None
62
+
63
+ logger.debug(
64
+ f"Verifying JWT token (length: {len(token)} chars), check_list={self.__use_list}"
65
+ )
66
+ try:
67
+ payload: dict[str, Any] = await await_maybe(
68
+ self._jam.jwt_verify_token(
69
+ token=token, check_exp=True, check_list=self.__use_list
70
+ )
71
+ )
72
+ logger.debug(
73
+ f"JWT token verified successfully, payload keys: {list(payload.keys())}"
74
+ )
75
+ except Exception as e:
76
+ logger.warning(f"Token verify error: {e}")
77
+ raise AuthenticationError("Token verification failed.")
78
+
79
+ return AuthCredentials(["authenticated"]), Payload(payload=payload)
80
+
81
+
82
+ class SessionBackend(AuthenticationBackend):
83
+ """Sessions backend for starlette."""
84
+
85
+ def __init__(
86
+ self,
87
+ jam: BaseJam,
88
+ cookie_name: str | None = "sessionId",
89
+ header_name: str | None = None,
90
+ ) -> None:
91
+ """Constructor.
92
+
93
+ Args:
94
+ jam (BaseJam): Jam instance
95
+ cookie_name (str | None): Session id cookie name
96
+ header_name (str | None): Session id header name
97
+ """
98
+ self._jam = jam
99
+ self.cookie_name = cookie_name
100
+ self.header_name = header_name
101
+
102
+ async def authenticate(
103
+ self, conn: HTTPConnection
104
+ ) -> tuple[AuthCredentials, BaseUser] | None:
105
+ """Starlette authentication handler."""
106
+ logger = self._jam._logger
107
+ session_id = None
108
+
109
+ logger.debug(
110
+ "SessionBackend: Attempting to extract session ID from request"
111
+ )
112
+ if self.cookie_name:
113
+ session_id = conn.cookies.get(self.cookie_name)
114
+ if session_id:
115
+ logger.debug(f"Session ID found in cookie '{self.cookie_name}'")
116
+
117
+ if not session_id and self.header_name:
118
+ header = conn.headers.get(self.header_name)
119
+ if header and header.startswith("Bearer "):
120
+ session_id = header.split("Bearer ")[1]
121
+ logger.debug(f"Session ID found in header '{self.header_name}'")
122
+
123
+ if not session_id:
124
+ logger.debug("No session ID found in request")
125
+ return None
126
+
127
+ logger.debug(f"Getting session data for session ID: {session_id}")
128
+ try:
129
+ payload: dict[str, Any] | None = await await_maybe( # type: ignore[arg-type]
130
+ self._jam.session_get(session_id) # type: ignore[arg-type]
131
+ )
132
+ if payload:
133
+ logger.debug(
134
+ f"Session data retrieved successfully, keys: {list(payload.keys())}"
135
+ )
136
+ else:
137
+ logger.debug(f"Session {session_id} not found")
138
+ # TODO: Return unauthized user
139
+ except Exception as e:
140
+ logger.warning(f"Session retrieval error: {e}")
141
+ raise AuthenticationError("Token verification failed.")
142
+
143
+ return AuthCredentials(["authenticated"]), Payload(payload=payload)
@@ -0,0 +1,18 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from typing import Any
4
+
5
+ from starlette.authentication import BaseUser
6
+
7
+
8
+ class Payload(BaseUser):
9
+ """Auth payload."""
10
+
11
+ def __init__(self, payload: dict[str, Any] | None):
12
+ """Auth payload."""
13
+ self.payload = payload
14
+
15
+ @property
16
+ def is_authenticated(self) -> bool:
17
+ """Auth checker."""
18
+ return True
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jamlib
3
- Version: 3.0.0b13.dev2
3
+ Version: 3.0.0b15
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
@@ -36,13 +36,14 @@ src/jam/ext/__init__.py
36
36
  src/jam/ext/fastapi/__init__.py
37
37
  src/jam/ext/flask/__init__.py
38
38
  src/jam/ext/flask/extensions.py
39
+ src/jam/ext/flask/objects.py
39
40
  src/jam/ext/litestar/__init__.py
40
41
  src/jam/ext/litestar/middleware.py
41
42
  src/jam/ext/litestar/objects.py
42
43
  src/jam/ext/litestar/plugins.py
43
44
  src/jam/ext/starlette/__init__.py
44
- src/jam/ext/starlette/backends.py
45
- src/jam/ext/starlette/objects.py
45
+ src/jam/ext/starlette/auth_backends.py
46
+ src/jam/ext/starlette/value.py
46
47
  src/jam/jwt/__algorithms__.py
47
48
  src/jam/jwt/__base__.py
48
49
  src/jam/jwt/__init__.py
@@ -1,11 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- """Flask integration.
4
-
5
- Flask docs: https://flask.palletsprojects.com
6
- """
7
-
8
- from .extensions import JamExtension, JWTExtension, SessionExtension
9
-
10
-
11
- __all__ = ["JamExtension", "JWTExtension", "SessionExtension"]