usso 0.23.2__tar.gz → 0.24.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.
Files changed (29) hide show
  1. {usso-0.23.2/src/usso.egg-info → usso-0.24.1}/PKG-INFO +2 -2
  2. {usso-0.23.2 → usso-0.24.1}/pyproject.toml +2 -2
  3. usso-0.24.1/src/usso/__init__.py +4 -0
  4. {usso-0.23.2 → usso-0.24.1}/src/usso/api.py +3 -6
  5. {usso-0.23.2 → usso-0.24.1}/src/usso/async_api.py +4 -5
  6. {usso-0.23.2 → usso-0.24.1}/src/usso/async_session.py +2 -3
  7. {usso-0.23.2 → usso-0.24.1}/src/usso/b64tools.py +1 -3
  8. usso-0.24.1/src/usso/django/middleware.py +88 -0
  9. usso-0.24.1/src/usso/fastapi/integration.py +56 -0
  10. usso-0.24.1/src/usso/package_data.dat +0 -0
  11. {usso-0.23.2 → usso-0.24.1/src/usso.egg-info}/PKG-INFO +2 -2
  12. {usso-0.23.2 → usso-0.24.1}/src/usso.egg-info/SOURCES.txt +2 -0
  13. usso-0.23.2/src/usso/__init__.py +0 -3
  14. usso-0.23.2/src/usso/fastapi/integration.py +0 -56
  15. {usso-0.23.2 → usso-0.24.1}/LICENSE.txt +0 -0
  16. {usso-0.23.2 → usso-0.24.1}/README.md +0 -0
  17. {usso-0.23.2 → usso-0.24.1}/setup.cfg +0 -0
  18. {usso-0.23.2 → usso-0.24.1}/src/usso/core.py +0 -0
  19. /usso-0.23.2/src/usso/package_data.dat → /usso-0.24.1/src/usso/django/__init__.py +0 -0
  20. {usso-0.23.2 → usso-0.24.1}/src/usso/exceptions.py +0 -0
  21. {usso-0.23.2 → usso-0.24.1}/src/usso/fastapi/__init__.py +0 -0
  22. {usso-0.23.2 → usso-0.24.1}/src/usso/session.py +0 -0
  23. {usso-0.23.2 → usso-0.24.1}/src/usso.egg-info/dependency_links.txt +0 -0
  24. {usso-0.23.2 → usso-0.24.1}/src/usso.egg-info/entry_points.txt +0 -0
  25. {usso-0.23.2 → usso-0.24.1}/src/usso.egg-info/requires.txt +0 -0
  26. {usso-0.23.2 → usso-0.24.1}/src/usso.egg-info/top_level.txt +0 -0
  27. {usso-0.23.2 → usso-0.24.1}/tests/test_api.py +0 -0
  28. {usso-0.23.2 → usso-0.24.1}/tests/test_core.py +0 -0
  29. {usso-0.23.2 → usso-0.24.1}/tests/test_simple.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: usso
3
- Version: 0.23.2
3
+ Version: 0.24.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>
@@ -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://github.com/ussoio/usso-python
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.23.2"
7
+ version = "0.24.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"
@@ -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://github.com/ussoio/usso-python"
43
+ "Say Thanks!" = "https://saythanks.io/to/mahdikiani"
44
44
  "Source" = "https://github.com/ussoio/usso-python"
45
45
 
46
46
  [project.scripts]
@@ -0,0 +1,4 @@
1
+ from .core import UserData, Usso
2
+ from .exceptions import USSOException
3
+
4
+ __all__ = ["UserData", "Usso", "USSOException"]
@@ -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
- import jwt
2
+
3
3
  import aiohttp
4
- import asyncio
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.23.2
3
+ Version: 0.24.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>
@@ -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://github.com/ussoio/usso-python
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
@@ -1,3 +0,0 @@
1
- from .core import UserData, Usso
2
-
3
- __all__ = ["UserData", "Usso"]
@@ -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