usso 0.25.0__tar.gz → 0.25.1__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.
- {usso-0.25.0/src/usso.egg-info → usso-0.25.1}/PKG-INFO +2 -2
- {usso-0.25.0 → usso-0.25.1}/pyproject.toml +3 -2
- {usso-0.25.0 → usso-0.25.1}/src/usso/core.py +53 -26
- {usso-0.25.0 → usso-0.25.1}/src/usso/fastapi/integration.py +2 -1
- {usso-0.25.0 → usso-0.25.1/src/usso.egg-info}/PKG-INFO +2 -2
- {usso-0.25.0 → usso-0.25.1}/src/usso.egg-info/SOURCES.txt +0 -2
- usso-0.25.0/src/usso/fastapi/auth_middleware.py +0 -87
- usso-0.25.0/src/usso/package_data.dat +0 -0
- {usso-0.25.0 → usso-0.25.1}/LICENSE.txt +0 -0
- {usso-0.25.0 → usso-0.25.1}/README.md +0 -0
- {usso-0.25.0 → usso-0.25.1}/setup.cfg +0 -0
- {usso-0.25.0 → usso-0.25.1}/src/usso/__init__.py +0 -0
- {usso-0.25.0 → usso-0.25.1}/src/usso/api.py +0 -0
- {usso-0.25.0 → usso-0.25.1}/src/usso/async_api.py +0 -0
- {usso-0.25.0 → usso-0.25.1}/src/usso/async_session.py +0 -0
- {usso-0.25.0 → usso-0.25.1}/src/usso/b64tools.py +0 -0
- {usso-0.25.0 → usso-0.25.1}/src/usso/django/__init__.py +0 -0
- {usso-0.25.0 → usso-0.25.1}/src/usso/django/middleware.py +0 -0
- {usso-0.25.0 → usso-0.25.1}/src/usso/exceptions.py +0 -0
- {usso-0.25.0 → usso-0.25.1}/src/usso/fastapi/__init__.py +0 -0
- {usso-0.25.0 → usso-0.25.1}/src/usso/session.py +0 -0
- {usso-0.25.0 → usso-0.25.1}/src/usso.egg-info/dependency_links.txt +0 -0
- {usso-0.25.0 → usso-0.25.1}/src/usso.egg-info/entry_points.txt +0 -0
- {usso-0.25.0 → usso-0.25.1}/src/usso.egg-info/requires.txt +1 -1
- {usso-0.25.0 → usso-0.25.1}/src/usso.egg-info/top_level.txt +0 -0
- {usso-0.25.0 → usso-0.25.1}/tests/test_api.py +0 -0
- {usso-0.25.0 → usso-0.25.1}/tests/test_core.py +0 -0
- {usso-0.25.0 → usso-0.25.1}/tests/test_simple.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: usso
|
3
|
-
Version: 0.25.
|
3
|
+
Version: 0.25.1
|
4
4
|
Summary: A plug-and-play client for integrating universal single sign-on (SSO) with Python frameworks, enabling secure and seamless authentication across microservices.
|
5
5
|
Author-email: Mahdi Kiani <mahdikiany@gmail.com>
|
6
6
|
Maintainer-email: Mahdi Kiani <mahdikiany@gmail.com>
|
@@ -46,10 +46,10 @@ Requires-Dist: pydantic>=2
|
|
46
46
|
Requires-Dist: requests>=2.26.0
|
47
47
|
Requires-Dist: pyjwt[crypto]
|
48
48
|
Requires-Dist: singleton_package
|
49
|
+
Requires-Dist: cachetools
|
49
50
|
Provides-Extra: fastapi
|
50
51
|
Requires-Dist: fastapi>=0.65.0; extra == "fastapi"
|
51
52
|
Requires-Dist: uvicorn[standard]>=0.13.0; extra == "fastapi"
|
52
|
-
Requires-Dist: cachetools; extra == "fastapi"
|
53
53
|
Provides-Extra: django
|
54
54
|
Requires-Dist: Django>=3.2; extra == "django"
|
55
55
|
Provides-Extra: dev
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "usso"
|
7
|
-
version = "0.25.
|
7
|
+
version = "0.25.1"
|
8
8
|
description = "A plug-and-play client for integrating universal single sign-on (SSO) with Python frameworks, enabling secure and seamless authentication across microservices."
|
9
9
|
readme = "README.md"
|
10
10
|
requires-python = ">=3.9"
|
@@ -32,8 +32,9 @@ dependencies = [
|
|
32
32
|
"requests>=2.26.0",
|
33
33
|
"pyjwt[crypto]",
|
34
34
|
"singleton_package",
|
35
|
+
"cachetools",
|
35
36
|
]
|
36
|
-
optional-dependencies = {"fastapi" = ["fastapi>=0.65.0", "uvicorn[standard]>=0.13.0"
|
37
|
+
optional-dependencies = {"fastapi" = ["fastapi>=0.65.0", "uvicorn[standard]>=0.13.0"],"django" = ["Django>=3.2"],"dev" = ["check-manifest"],"test" = ["coverage"]}
|
37
38
|
|
38
39
|
[project.urls]
|
39
40
|
"Homepage" = "https://github.com/ussoio/usso-python"
|
@@ -1,8 +1,8 @@
|
|
1
|
+
import json
|
1
2
|
import logging
|
2
3
|
import os
|
3
4
|
import uuid
|
4
5
|
from functools import lru_cache
|
5
|
-
from typing import Optional, Tuple
|
6
6
|
|
7
7
|
import cachetools.func
|
8
8
|
import jwt
|
@@ -142,33 +142,60 @@ class JWTConfig(BaseModel):
|
|
142
142
|
return decode_token(self.secret, token, algorithms=[self.type])
|
143
143
|
|
144
144
|
|
145
|
-
class Usso
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
145
|
+
class Usso:
|
146
|
+
|
147
|
+
def __init__(
|
148
|
+
self,
|
149
|
+
*,
|
150
|
+
jwt_config: str | dict | JWTConfig | None = None,
|
151
|
+
jwt_configs: list[str] | list[dict] | list[JWTConfig] | None = None,
|
152
|
+
):
|
153
|
+
if jwt_config is None and jwt_configs is None:
|
154
|
+
jwt_config = os.getenv("USSO_JWT_CONFIG")
|
155
|
+
|
156
|
+
if jwt_config is None and jwt_configs is None:
|
157
|
+
jwk_url = os.getenv("USSO_JWK_URL") or os.getenv("USSO_JWKS_URL")
|
158
|
+
if not jwk_url:
|
159
|
+
self.jwt_configs = [JWTConfig(jwk_url=jwk_url)]
|
160
|
+
return
|
161
|
+
|
162
|
+
raise ValueError(
|
163
|
+
"\n".join(
|
164
|
+
[
|
165
|
+
"Either jwt_config or jwt_configs must be provided",
|
166
|
+
"or set the environment variable USSO_JWT_CONFIG or USSO_JWK_URL",
|
167
|
+
]
|
168
|
+
)
|
169
|
+
)
|
170
|
+
|
171
|
+
def _get_config(jwt_config):
|
172
|
+
if isinstance(jwt_config, str):
|
173
|
+
jwt_config = json.loads(jwt_config)
|
174
|
+
if isinstance(jwt_config, dict):
|
175
|
+
jwt_config = JWTConfig(**jwt_config)
|
176
|
+
return jwt_config
|
150
177
|
|
151
|
-
|
152
|
-
|
178
|
+
if isinstance(jwt_config, str | dict | JWTConfig):
|
179
|
+
jwt_config = [_get_config(jwt_config)]
|
180
|
+
elif isinstance(jwt_config, list):
|
181
|
+
jwt_config = [_get_config(j) for j in jwt_config]
|
153
182
|
|
154
|
-
|
155
|
-
self
|
156
|
-
) -> Tuple[str, str]:
|
157
|
-
return get_authorization_scheme_param(authorization_header_value)
|
183
|
+
# self.jwk_url = jwt_config
|
184
|
+
self.jwt_configs = jwt_config
|
158
185
|
|
159
186
|
def user_data_from_token(self, token: str, **kwargs) -> UserData | None:
|
160
187
|
"""Return the user associated with a token value."""
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
188
|
+
exp = None
|
189
|
+
for jwk_config in self.jwt_configs:
|
190
|
+
try:
|
191
|
+
return jwk_config.decode(token)
|
192
|
+
except USSOException as e:
|
193
|
+
exp = e
|
194
|
+
|
195
|
+
if kwargs.get("raise_exception", True):
|
196
|
+
if exp:
|
197
|
+
raise exp
|
198
|
+
raise USSOException(
|
199
|
+
status_code=401,
|
200
|
+
error="unauthorized",
|
201
|
+
)
|
@@ -3,9 +3,10 @@ import logging
|
|
3
3
|
from fastapi import Request, WebSocket
|
4
4
|
from starlette.status import HTTP_401_UNAUTHORIZED
|
5
5
|
|
6
|
-
from usso.core import UserData, Usso
|
7
6
|
from usso.exceptions import USSOException
|
8
7
|
|
8
|
+
from ..core import UserData, Usso
|
9
|
+
|
9
10
|
logger = logging.getLogger("usso")
|
10
11
|
|
11
12
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: usso
|
3
|
-
Version: 0.25.
|
3
|
+
Version: 0.25.1
|
4
4
|
Summary: A plug-and-play client for integrating universal single sign-on (SSO) with Python frameworks, enabling secure and seamless authentication across microservices.
|
5
5
|
Author-email: Mahdi Kiani <mahdikiany@gmail.com>
|
6
6
|
Maintainer-email: Mahdi Kiani <mahdikiany@gmail.com>
|
@@ -46,10 +46,10 @@ Requires-Dist: pydantic>=2
|
|
46
46
|
Requires-Dist: requests>=2.26.0
|
47
47
|
Requires-Dist: pyjwt[crypto]
|
48
48
|
Requires-Dist: singleton_package
|
49
|
+
Requires-Dist: cachetools
|
49
50
|
Provides-Extra: fastapi
|
50
51
|
Requires-Dist: fastapi>=0.65.0; extra == "fastapi"
|
51
52
|
Requires-Dist: uvicorn[standard]>=0.13.0; extra == "fastapi"
|
52
|
-
Requires-Dist: cachetools; extra == "fastapi"
|
53
53
|
Provides-Extra: django
|
54
54
|
Requires-Dist: Django>=3.2; extra == "django"
|
55
55
|
Provides-Extra: dev
|
@@ -8,7 +8,6 @@ src/usso/async_session.py
|
|
8
8
|
src/usso/b64tools.py
|
9
9
|
src/usso/core.py
|
10
10
|
src/usso/exceptions.py
|
11
|
-
src/usso/package_data.dat
|
12
11
|
src/usso/session.py
|
13
12
|
src/usso.egg-info/PKG-INFO
|
14
13
|
src/usso.egg-info/SOURCES.txt
|
@@ -19,7 +18,6 @@ src/usso.egg-info/top_level.txt
|
|
19
18
|
src/usso/django/__init__.py
|
20
19
|
src/usso/django/middleware.py
|
21
20
|
src/usso/fastapi/__init__.py
|
22
|
-
src/usso/fastapi/auth_middleware.py
|
23
21
|
src/usso/fastapi/integration.py
|
24
22
|
tests/test_api.py
|
25
23
|
tests/test_core.py
|
@@ -1,87 +0,0 @@
|
|
1
|
-
import json
|
2
|
-
import logging
|
3
|
-
import os
|
4
|
-
|
5
|
-
from fastapi import Request, WebSocket
|
6
|
-
from starlette.status import HTTP_401_UNAUTHORIZED
|
7
|
-
|
8
|
-
from usso.exceptions import USSOException
|
9
|
-
|
10
|
-
from ..core import JWTConfig, UserData
|
11
|
-
from .integration import get_request_token
|
12
|
-
|
13
|
-
logger = logging.getLogger("usso")
|
14
|
-
|
15
|
-
|
16
|
-
class Usso:
|
17
|
-
|
18
|
-
def __init__(
|
19
|
-
self,
|
20
|
-
jwt_config: (
|
21
|
-
str | dict | JWTConfig | list[str] | list[dict] | list[JWTConfig] | None
|
22
|
-
) = None,
|
23
|
-
):
|
24
|
-
if jwt_config is None:
|
25
|
-
self.jwk_url = os.getenv("USSO_JWK_URL")
|
26
|
-
self.jwt_configs = [JWTConfig(jwk_url=self.jwk_url)]
|
27
|
-
return
|
28
|
-
|
29
|
-
def _get_config(jwt_config):
|
30
|
-
if isinstance(jwt_config, str):
|
31
|
-
jwt_config = json.loads(jwt_config)
|
32
|
-
if isinstance(jwt_config, dict):
|
33
|
-
jwt_config = JWTConfig(**jwt_config)
|
34
|
-
return jwt_config
|
35
|
-
|
36
|
-
if isinstance(jwt_config, str | dict | JWTConfig):
|
37
|
-
jwt_config = [_get_config(jwt_config)]
|
38
|
-
elif isinstance(jwt_config, list):
|
39
|
-
jwt_config = [_get_config(j) for j in jwt_config]
|
40
|
-
|
41
|
-
# self.jwk_url = jwt_config
|
42
|
-
self.jwt_configs = jwt_config
|
43
|
-
|
44
|
-
def user_data_from_token(self, token: str, **kwargs) -> UserData | None:
|
45
|
-
"""Return the user associated with a token value."""
|
46
|
-
exp = None
|
47
|
-
for jwk_config in self.jwt_configs:
|
48
|
-
try:
|
49
|
-
return jwk_config.decode(token)
|
50
|
-
except USSOException as e:
|
51
|
-
exp = e
|
52
|
-
|
53
|
-
if kwargs.get("raise_exception", True):
|
54
|
-
if exp:
|
55
|
-
raise exp
|
56
|
-
raise USSOException(
|
57
|
-
status_code=HTTP_401_UNAUTHORIZED,
|
58
|
-
error="unauthorized",
|
59
|
-
)
|
60
|
-
|
61
|
-
async def jwt_access_security(self, request: Request, **kwargs) -> UserData | None:
|
62
|
-
"""Return the user associated with a token value."""
|
63
|
-
token = get_request_token(request)
|
64
|
-
if token:
|
65
|
-
return self.user_data_from_token(token)
|
66
|
-
|
67
|
-
if kwargs.get("raise_exception", True):
|
68
|
-
raise USSOException(
|
69
|
-
status_code=HTTP_401_UNAUTHORIZED,
|
70
|
-
error="unauthorized",
|
71
|
-
)
|
72
|
-
return None
|
73
|
-
|
74
|
-
async def jwt_access_security_ws(
|
75
|
-
self, websocket: WebSocket, **kwargs
|
76
|
-
) -> UserData | None:
|
77
|
-
"""Return the user associated with a token value."""
|
78
|
-
token = get_request_token(websocket)
|
79
|
-
if token:
|
80
|
-
return self.user_data_from_token(token)
|
81
|
-
|
82
|
-
if kwargs.get("raise_exception", True):
|
83
|
-
raise USSOException(
|
84
|
-
status_code=HTTP_401_UNAUTHORIZED,
|
85
|
-
error="unauthorized",
|
86
|
-
)
|
87
|
-
return None
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|