usso 0.23.2__tar.gz → 0.24.0__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.23.2/src/usso.egg-info → usso-0.24.0}/PKG-INFO +2 -2
- {usso-0.23.2 → usso-0.24.0}/pyproject.toml +2 -2
- usso-0.24.0/src/usso/__init__.py +4 -0
- {usso-0.23.2 → usso-0.24.0}/src/usso/api.py +3 -6
- {usso-0.23.2 → usso-0.24.0}/src/usso/async_api.py +4 -5
- {usso-0.23.2 → usso-0.24.0}/src/usso/async_session.py +2 -3
- {usso-0.23.2 → usso-0.24.0}/src/usso/b64tools.py +1 -3
- usso-0.24.0/src/usso/django/middleware.py +88 -0
- usso-0.24.0/src/usso/fastapi/integration.py +56 -0
- usso-0.24.0/src/usso/package_data.dat +0 -0
- {usso-0.23.2 → usso-0.24.0/src/usso.egg-info}/PKG-INFO +2 -2
- {usso-0.23.2 → usso-0.24.0}/src/usso.egg-info/SOURCES.txt +2 -0
- usso-0.23.2/src/usso/__init__.py +0 -3
- usso-0.23.2/src/usso/fastapi/integration.py +0 -56
- {usso-0.23.2 → usso-0.24.0}/LICENSE.txt +0 -0
- {usso-0.23.2 → usso-0.24.0}/README.md +0 -0
- {usso-0.23.2 → usso-0.24.0}/setup.cfg +0 -0
- {usso-0.23.2 → usso-0.24.0}/src/usso/core.py +0 -0
- /usso-0.23.2/src/usso/package_data.dat → /usso-0.24.0/src/usso/django/__init__.py +0 -0
- {usso-0.23.2 → usso-0.24.0}/src/usso/exceptions.py +0 -0
- {usso-0.23.2 → usso-0.24.0}/src/usso/fastapi/__init__.py +0 -0
- {usso-0.23.2 → usso-0.24.0}/src/usso/session.py +0 -0
- {usso-0.23.2 → usso-0.24.0}/src/usso.egg-info/dependency_links.txt +0 -0
- {usso-0.23.2 → usso-0.24.0}/src/usso.egg-info/entry_points.txt +0 -0
- {usso-0.23.2 → usso-0.24.0}/src/usso.egg-info/requires.txt +0 -0
- {usso-0.23.2 → usso-0.24.0}/src/usso.egg-info/top_level.txt +0 -0
- {usso-0.23.2 → usso-0.24.0}/tests/test_api.py +0 -0
- {usso-0.23.2 → usso-0.24.0}/tests/test_core.py +0 -0
- {usso-0.23.2 → usso-0.24.0}/tests/test_simple.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: usso
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.24.0
|
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>
|
@@ -27,7 +27,7 @@ License: Copyright (c) 2016 The Python Packaging Authority (PyPA)
|
|
27
27
|
Project-URL: Homepage, https://github.com/ussoio/usso-python
|
28
28
|
Project-URL: Bug Reports, https://github.com/ussoio/usso-python/issues
|
29
29
|
Project-URL: Funding, https://github.com/ussoio/usso-python
|
30
|
-
Project-URL: Say Thanks!, https://
|
30
|
+
Project-URL: Say Thanks!, https://saythanks.io/to/mahdikiani
|
31
31
|
Project-URL: Source, https://github.com/ussoio/usso-python
|
32
32
|
Keywords: usso,sso,authentication,security,fastapi,django
|
33
33
|
Classifier: Development Status :: 3 - Alpha
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "usso"
|
7
|
-
version = "0.
|
7
|
+
version = "0.24.0"
|
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"
|
@@ -40,7 +40,7 @@ optional-dependencies = {"fastapi" = ["fastapi>=0.65.0", "uvicorn[standard]>=0.1
|
|
40
40
|
"Homepage" = "https://github.com/ussoio/usso-python"
|
41
41
|
"Bug Reports" = "https://github.com/ussoio/usso-python/issues"
|
42
42
|
"Funding" = "https://github.com/ussoio/usso-python"
|
43
|
-
"Say Thanks!" = "https://
|
43
|
+
"Say Thanks!" = "https://saythanks.io/to/mahdikiani"
|
44
44
|
"Source" = "https://github.com/ussoio/usso-python"
|
45
45
|
|
46
46
|
[project.scripts]
|
@@ -2,6 +2,7 @@ import logging
|
|
2
2
|
|
3
3
|
import requests
|
4
4
|
from singleton import Singleton
|
5
|
+
|
5
6
|
from usso.core import UserData, Usso
|
6
7
|
|
7
8
|
|
@@ -87,9 +88,7 @@ class UssoAPI(metaclass=Singleton):
|
|
87
88
|
def get_users(self, **kwargs) -> list[UserData]:
|
88
89
|
users_dict = self._request(endpoint="website/users", **kwargs)
|
89
90
|
|
90
|
-
return [
|
91
|
-
UserData(user_id=user.get("uid"), **user) for user in users_dict
|
92
|
-
]
|
91
|
+
return [UserData(user_id=user.get("uid"), **user) for user in users_dict]
|
93
92
|
|
94
93
|
def get_user(self, user_id: str, **kwargs) -> UserData:
|
95
94
|
user_dict = self._request(
|
@@ -145,9 +144,7 @@ class UssoAPI(metaclass=Singleton):
|
|
145
144
|
return UserData(user_id=user_dict.get("uid"), **user_dict)
|
146
145
|
|
147
146
|
def get_user_payload(self, user_id: str, **kwargs) -> dict:
|
148
|
-
return self._request(
|
149
|
-
endpoint=f"website/users/{user_id}/payload", **kwargs
|
150
|
-
)
|
147
|
+
return self._request(endpoint=f"website/users/{user_id}/payload", **kwargs)
|
151
148
|
|
152
149
|
def update_user_payload(
|
153
150
|
self,
|
@@ -2,6 +2,7 @@ import logging
|
|
2
2
|
|
3
3
|
import aiohttp
|
4
4
|
from singleton import Singleton
|
5
|
+
|
5
6
|
from usso.core import UserData, Usso
|
6
7
|
|
7
8
|
|
@@ -39,7 +40,7 @@ class AsyncUssoAPI(metaclass=Singleton):
|
|
39
40
|
if kwargs.get("raise_exception", True):
|
40
41
|
resp.raise_for_status()
|
41
42
|
self.access_token = (await resp.json()).get("access_token")
|
42
|
-
|
43
|
+
|
43
44
|
def _access_valid(self) -> bool:
|
44
45
|
if not self.access_token:
|
45
46
|
return False
|
@@ -86,13 +87,11 @@ class AsyncUssoAPI(metaclass=Singleton):
|
|
86
87
|
logging.error(f"Response: {resp.text}")
|
87
88
|
raise e
|
88
89
|
return await resp.json()
|
89
|
-
|
90
|
+
|
90
91
|
async def get_users(self, **kwargs) -> list[UserData]:
|
91
92
|
users_dict = await self._request(endpoint="website/users", **kwargs)
|
92
93
|
|
93
|
-
return [
|
94
|
-
UserData(user_id=user.get("uid"), **user) for user in users_dict
|
95
|
-
]
|
94
|
+
return [UserData(user_id=user.get("uid"), **user) for user in users_dict]
|
96
95
|
|
97
96
|
async def get_user(self, user_id: str, **kwargs) -> UserData:
|
98
97
|
user_dict = await self._request(
|
@@ -1,7 +1,7 @@
|
|
1
1
|
from datetime import datetime
|
2
|
-
|
2
|
+
|
3
3
|
import aiohttp
|
4
|
-
import
|
4
|
+
import jwt
|
5
5
|
|
6
6
|
|
7
7
|
class AsyncUssoSession(aiohttp.ClientSession):
|
@@ -46,4 +46,3 @@ class AsyncUssoSession(aiohttp.ClientSession):
|
|
46
46
|
|
47
47
|
async def __aexit__(self, exc_type, exc_value, traceback):
|
48
48
|
await self.close()
|
49
|
-
|
@@ -3,9 +3,7 @@ import uuid
|
|
3
3
|
|
4
4
|
|
5
5
|
def b64_encode_uuid(uuid_str: uuid.UUID | str):
|
6
|
-
uuid_UUID = (
|
7
|
-
uuid_str if isinstance(uuid_str, uuid.UUID) else uuid.UUID(uuid_str)
|
8
|
-
)
|
6
|
+
uuid_UUID = uuid_str if isinstance(uuid_str, uuid.UUID) else uuid.UUID(uuid_str)
|
9
7
|
uuid_bytes = uuid_UUID.bytes
|
10
8
|
encoded_uuid = base64.urlsafe_b64encode(uuid_bytes).decode()
|
11
9
|
return encoded_uuid
|
@@ -0,0 +1,88 @@
|
|
1
|
+
import logging
|
2
|
+
|
3
|
+
from django.conf import settings
|
4
|
+
from django.contrib.auth.models import User
|
5
|
+
from django.db.utils import IntegrityError
|
6
|
+
from django.http import JsonResponse
|
7
|
+
from django.http.request import HttpRequest
|
8
|
+
from django.utils.deprecation import MiddlewareMixin
|
9
|
+
|
10
|
+
from usso import UserData, Usso, USSOException
|
11
|
+
|
12
|
+
logger = logging.getLogger("usso")
|
13
|
+
|
14
|
+
|
15
|
+
class USSOAuthenticationMiddleware(MiddlewareMixin):
|
16
|
+
|
17
|
+
def process_request(self, request: HttpRequest):
|
18
|
+
"""
|
19
|
+
Middleware to authenticate users by JWT token and create or return a user in the database.
|
20
|
+
"""
|
21
|
+
try:
|
22
|
+
logger.debug("Processing request in USSO authentication middleware")
|
23
|
+
|
24
|
+
# Extract and verify JWT from Authorization header or cookies
|
25
|
+
user_data = self.jwt_access_security(request)
|
26
|
+
|
27
|
+
if user_data:
|
28
|
+
# Check database for the user or create a new one
|
29
|
+
user = self.get_or_create_user(user_data)
|
30
|
+
# Attach the user to the request
|
31
|
+
request.user = user
|
32
|
+
else:
|
33
|
+
return JsonResponse(
|
34
|
+
{"error": "Authentication failed: No valid JWT token provided."},
|
35
|
+
status=401,
|
36
|
+
)
|
37
|
+
except USSOException as e:
|
38
|
+
# Handle any errors raised by USSO authentication
|
39
|
+
return JsonResponse({"error": str(e)}, status=401)
|
40
|
+
|
41
|
+
def get_request_token(self, request: HttpRequest) -> str | None:
|
42
|
+
authorization = request.headers.get("Authorization")
|
43
|
+
if authorization:
|
44
|
+
scheme, credentials = Usso(
|
45
|
+
jwks_url=settings.USSO_JWK_URL
|
46
|
+
).get_authorization_scheme_param(authorization)
|
47
|
+
if scheme.lower() == "bearer":
|
48
|
+
return credentials # Bearer token
|
49
|
+
|
50
|
+
return request.COOKIES.get("usso_access_token")
|
51
|
+
|
52
|
+
def jwt_access_security(self, request: HttpRequest) -> UserData | None:
|
53
|
+
"""Return the user associated with a token value."""
|
54
|
+
token = self.get_request_token(request)
|
55
|
+
if not token:
|
56
|
+
raise USSOException(
|
57
|
+
status_code=401,
|
58
|
+
error="Unauthorized",
|
59
|
+
)
|
60
|
+
|
61
|
+
# Get user data from the token
|
62
|
+
return Usso(jwks_url=settings.USSO_JWK_URL).user_data_from_token(token)
|
63
|
+
|
64
|
+
def get_or_create_user(self, user_data: UserData) -> User:
|
65
|
+
"""
|
66
|
+
Check if a user exists by phone. If not, create a new user and return it.
|
67
|
+
"""
|
68
|
+
phone = user_data.phone
|
69
|
+
email = user_data.email or f"{user_data.user_id}@example.com" # Fallback email
|
70
|
+
|
71
|
+
try:
|
72
|
+
# Try to get the user by phone
|
73
|
+
user, created = User.objects.get_or_create(
|
74
|
+
username=phone,
|
75
|
+
defaults={
|
76
|
+
"first_name": user_data.user_id,
|
77
|
+
"email": email,
|
78
|
+
},
|
79
|
+
)
|
80
|
+
|
81
|
+
if created:
|
82
|
+
logger.info(f"New user created with phone: {phone}")
|
83
|
+
|
84
|
+
return user
|
85
|
+
|
86
|
+
except IntegrityError as e:
|
87
|
+
logger.error(f"Integrity error while creating user: {str(e)}")
|
88
|
+
raise ValueError(f"Error while creating user: {str(e)}")
|
@@ -0,0 +1,56 @@
|
|
1
|
+
import logging
|
2
|
+
|
3
|
+
from fastapi import Request, WebSocket
|
4
|
+
from starlette.status import HTTP_401_UNAUTHORIZED
|
5
|
+
|
6
|
+
from usso.core import UserData, Usso
|
7
|
+
from usso.exceptions import USSOException
|
8
|
+
|
9
|
+
logger = logging.getLogger("usso")
|
10
|
+
|
11
|
+
|
12
|
+
def get_request_token(request: Request | WebSocket) -> UserData | None:
|
13
|
+
authorization = request.headers.get("Authorization")
|
14
|
+
if authorization:
|
15
|
+
scheme, credentials = Usso().get_authorization_scheme_param(authorization)
|
16
|
+
if scheme.lower() == "bearer":
|
17
|
+
token = credentials
|
18
|
+
|
19
|
+
else:
|
20
|
+
cookie_token = request.cookies.get("usso_access_token")
|
21
|
+
if cookie_token:
|
22
|
+
token = cookie_token
|
23
|
+
|
24
|
+
return token
|
25
|
+
|
26
|
+
|
27
|
+
def jwt_access_security_None(request: Request) -> UserData | None:
|
28
|
+
"""Return the user associated with a token value."""
|
29
|
+
token = get_request_token(request)
|
30
|
+
if not token:
|
31
|
+
return None
|
32
|
+
return Usso().user_data_from_token(token, raise_exception=False)
|
33
|
+
|
34
|
+
|
35
|
+
def jwt_access_security(request: Request) -> UserData | None:
|
36
|
+
"""Return the user associated with a token value."""
|
37
|
+
token = get_request_token(request)
|
38
|
+
if not token:
|
39
|
+
raise USSOException(
|
40
|
+
status_code=HTTP_401_UNAUTHORIZED,
|
41
|
+
error="unauthorized",
|
42
|
+
)
|
43
|
+
|
44
|
+
return Usso().user_data_from_token(token)
|
45
|
+
|
46
|
+
|
47
|
+
def jwt_access_security_ws(websocket: WebSocket) -> UserData | None:
|
48
|
+
"""Return the user associated with a token value."""
|
49
|
+
token = get_request_token(websocket)
|
50
|
+
if not token:
|
51
|
+
raise USSOException(
|
52
|
+
status_code=HTTP_401_UNAUTHORIZED,
|
53
|
+
error="unauthorized",
|
54
|
+
)
|
55
|
+
|
56
|
+
return Usso().user_data_from_token(token)
|
File without changes
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: usso
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.24.0
|
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>
|
@@ -27,7 +27,7 @@ License: Copyright (c) 2016 The Python Packaging Authority (PyPA)
|
|
27
27
|
Project-URL: Homepage, https://github.com/ussoio/usso-python
|
28
28
|
Project-URL: Bug Reports, https://github.com/ussoio/usso-python/issues
|
29
29
|
Project-URL: Funding, https://github.com/ussoio/usso-python
|
30
|
-
Project-URL: Say Thanks!, https://
|
30
|
+
Project-URL: Say Thanks!, https://saythanks.io/to/mahdikiani
|
31
31
|
Project-URL: Source, https://github.com/ussoio/usso-python
|
32
32
|
Keywords: usso,sso,authentication,security,fastapi,django
|
33
33
|
Classifier: Development Status :: 3 - Alpha
|
@@ -16,6 +16,8 @@ src/usso.egg-info/dependency_links.txt
|
|
16
16
|
src/usso.egg-info/entry_points.txt
|
17
17
|
src/usso.egg-info/requires.txt
|
18
18
|
src/usso.egg-info/top_level.txt
|
19
|
+
src/usso/django/__init__.py
|
20
|
+
src/usso/django/middleware.py
|
19
21
|
src/usso/fastapi/__init__.py
|
20
22
|
src/usso/fastapi/integration.py
|
21
23
|
tests/test_api.py
|
usso-0.23.2/src/usso/__init__.py
DELETED
@@ -1,56 +0,0 @@
|
|
1
|
-
import logging
|
2
|
-
|
3
|
-
from fastapi import Request, WebSocket
|
4
|
-
from starlette.status import HTTP_401_UNAUTHORIZED
|
5
|
-
from usso.core import UserData, Usso
|
6
|
-
from usso.exceptions import USSOException
|
7
|
-
|
8
|
-
logger = logging.getLogger("usso")
|
9
|
-
|
10
|
-
|
11
|
-
async def jwt_access_security(request: Request) -> UserData | None:
|
12
|
-
"""Return the user associated with a token value."""
|
13
|
-
kwargs = {}
|
14
|
-
authorization = request.headers.get("Authorization")
|
15
|
-
if authorization:
|
16
|
-
scheme, credentials = Usso().get_authorization_scheme_param(
|
17
|
-
authorization
|
18
|
-
)
|
19
|
-
if scheme.lower() == "bearer":
|
20
|
-
token = credentials
|
21
|
-
return Usso().user_data_from_token(token, **kwargs)
|
22
|
-
|
23
|
-
cookie_token = request.cookies.get("usso_access_token")
|
24
|
-
if cookie_token:
|
25
|
-
return Usso().user_data_from_token(cookie_token, **kwargs)
|
26
|
-
|
27
|
-
if kwargs.get("raise_exception", True):
|
28
|
-
raise USSOException(
|
29
|
-
status_code=HTTP_401_UNAUTHORIZED,
|
30
|
-
error="unauthorized",
|
31
|
-
)
|
32
|
-
return None
|
33
|
-
|
34
|
-
|
35
|
-
async def jwt_access_security_ws(websocket: WebSocket) -> UserData | None:
|
36
|
-
"""Return the user associated with a token value."""
|
37
|
-
kwargs = {}
|
38
|
-
authorization = websocket.headers.get("Authorization")
|
39
|
-
if authorization:
|
40
|
-
scheme, credentials = Usso().get_authorization_scheme_param(
|
41
|
-
authorization
|
42
|
-
)
|
43
|
-
if scheme.lower() == "bearer":
|
44
|
-
token = credentials
|
45
|
-
return Usso().user_data_from_token(token, **kwargs)
|
46
|
-
|
47
|
-
cookie_token = websocket.cookies.get("usso_access_token")
|
48
|
-
if cookie_token:
|
49
|
-
return Usso().user_data_from_token(cookie_token, **kwargs)
|
50
|
-
|
51
|
-
if kwargs.get("raise_exception", True):
|
52
|
-
raise USSOException(
|
53
|
-
status_code=HTTP_401_UNAUTHORIZED,
|
54
|
-
error="unauthorized",
|
55
|
-
)
|
56
|
-
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
|