usso 0.21.2__py3-none-any.whl → 0.23.0__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.
usso/async_api.py ADDED
@@ -0,0 +1,179 @@
1
+ import logging
2
+
3
+ import aiohttp
4
+ from singleton import Singleton
5
+ from usso.core import UserData, Usso
6
+
7
+
8
+ class AsyncUssoAPI(metaclass=Singleton):
9
+ def __init__(
10
+ self,
11
+ url: str = "https://api.usso.io",
12
+ api_key: str = None,
13
+ refresh_token: str = None,
14
+ ):
15
+ if url and not url.startswith("http"):
16
+ url = f"https://{url}"
17
+ self.url = url
18
+ assert (
19
+ api_key or refresh_token
20
+ ), "Either api_key or refresh_token must be provided"
21
+ self.api_key = api_key
22
+ self.refresh_token = refresh_token
23
+ self.access_token = None
24
+
25
+ async def _refresh(self, **kwargs):
26
+ if not self.refresh_token:
27
+ return
28
+
29
+ url = f"{self.url}/auth/refresh"
30
+
31
+ if self.refresh_token:
32
+ headers = {
33
+ "Authorization": f"Bearer {self.refresh_token}",
34
+ "content-type": "application/json",
35
+ }
36
+
37
+ async with aiohttp.ClientSession() as session:
38
+ async with session.post(url, headers=headers) as resp:
39
+ if kwargs.get("raise_exception", True):
40
+ resp.raise_for_status()
41
+ self.access_token = (await resp.json()).get("access_token")
42
+
43
+ def _access_valid(self) -> bool:
44
+ if not self.access_token:
45
+ return False
46
+
47
+ user_data = Usso(
48
+ jwks_url=f"{self.url}/website/jwks.json?"
49
+ ).user_data_from_token(self.access_token)
50
+ if user_data:
51
+ return True
52
+ return False
53
+
54
+ async def _request(
55
+ self,
56
+ method="get",
57
+ endpoint: str = "",
58
+ data: dict = None,
59
+ **kwargs,
60
+ ) -> dict:
61
+ url = f"{self.url}/{endpoint}"
62
+ headers = {"content-type": "application/json"}
63
+ if self.api_key:
64
+ headers["x-api-key"] = self.api_key
65
+ elif self.refresh_token:
66
+ if not self.access_token:
67
+ await self._refresh()
68
+ headers["Authorization"] = f"Bearer {self.access_token}"
69
+
70
+ async with aiohttp.ClientSession() as session:
71
+ async with session.request(
72
+ method,
73
+ url,
74
+ headers=headers,
75
+ json=data,
76
+ ) as resp:
77
+ try:
78
+ resp_json = resp.json()
79
+ resp.raise_for_status()
80
+ except aiohttp.ClientError as e:
81
+ logging.error(f"Error: {e}")
82
+ logging.error(f"Response: {resp_json}")
83
+ raise e
84
+ except Exception as e:
85
+ logging.error(f"Error: {e}")
86
+ logging.error(f"Response: {resp.text}")
87
+ raise e
88
+ return await resp.json()
89
+
90
+ async def get_users(self, **kwargs) -> list[UserData]:
91
+ users_dict = await self._request(endpoint="website/users", **kwargs)
92
+
93
+ return [
94
+ UserData(user_id=user.get("uid"), **user) for user in users_dict
95
+ ]
96
+
97
+ async def get_user(self, user_id: str, **kwargs) -> UserData:
98
+ user_dict = await self._request(
99
+ endpoint=f"website/users/{user_id}",
100
+ **kwargs,
101
+ )
102
+ return UserData(user_id=user_dict.get("uid"), **user_dict)
103
+
104
+ async def get_user_by_credentials(self, credentials: dict, **kwargs) -> UserData:
105
+ user_dict = await self._request(
106
+ endpoint="website/users/credentials",
107
+ data=credentials,
108
+ **kwargs,
109
+ )
110
+ return UserData(user_id=user_dict.get("uid"), **user_dict)
111
+
112
+ async def create_user(self, user_data: dict, **kwargs) -> UserData:
113
+ user_dict = await self._request(
114
+ method="post",
115
+ endpoint="website/users",
116
+ data=user_data,
117
+ **kwargs,
118
+ )
119
+
120
+ return UserData(user_id=user_dict.get("uid"), **user_dict)
121
+
122
+ async def create_user_credentials(
123
+ self, user_id: str, credentials: dict, **kwargs
124
+ ) -> UserData:
125
+ user_dict = await self._request(
126
+ method="post",
127
+ endpoint=f"website/users/{user_id}/credentials",
128
+ data=credentials,
129
+ **kwargs,
130
+ )
131
+ return UserData(user_id=user_dict.get("uid"), **user_dict)
132
+
133
+ async def create_user_by_credentials(
134
+ self,
135
+ user_data: dict | None = None,
136
+ credentials: dict | None = None,
137
+ **kwargs,
138
+ ) -> UserData:
139
+ user_data = user_data or {}
140
+ if credentials:
141
+ user_data["authenticators"] = [credentials]
142
+ user_dict = await self._request(
143
+ method="post",
144
+ endpoint="website/users",
145
+ data=credentials,
146
+ **kwargs,
147
+ )
148
+ return UserData(user_id=user_dict.get("uid"), **user_dict)
149
+
150
+ async def get_user_payload(self, user_id: str, **kwargs) -> dict:
151
+ return await self._request(
152
+ endpoint=f"website/users/{user_id}/payload", **kwargs
153
+ )
154
+
155
+ async def update_user_payload(
156
+ self,
157
+ user_id: str,
158
+ payload: dict,
159
+ **kwargs,
160
+ ) -> dict:
161
+ return await self._request(
162
+ method="patch",
163
+ endpoint=f"website/users/{user_id}/payload",
164
+ data=payload,
165
+ **kwargs,
166
+ )
167
+
168
+ async def set_user_payload(
169
+ self,
170
+ user_id: str,
171
+ payload: dict,
172
+ **kwargs,
173
+ ) -> dict:
174
+ return await self._request(
175
+ method="put",
176
+ endpoint=f"website/users/{user_id}/payload",
177
+ data=payload,
178
+ **kwargs,
179
+ )
usso/async_session.py ADDED
@@ -0,0 +1,49 @@
1
+ from datetime import datetime
2
+ import jwt
3
+ import aiohttp
4
+ import asyncio
5
+
6
+
7
+ class AsyncUssoSession(aiohttp.ClientSession):
8
+ def __init__(
9
+ self, sso_refresh_url: str, refresh_token: str | None = None, **kwargs
10
+ ):
11
+ super().__init__(**kwargs)
12
+ self.sso_refresh_url = sso_refresh_url
13
+ self.refresh_token = refresh_token
14
+ self.access_token = None
15
+
16
+ async def _refresh(self):
17
+ if not self.refresh_token:
18
+ raise ValueError("Refresh token not provided or invalid.")
19
+
20
+ async with aiohttp.ClientSession() as session:
21
+ async with session.post(
22
+ self.sso_refresh_url,
23
+ json={"refresh_token": self.refresh_token},
24
+ ) as response:
25
+ response.raise_for_status()
26
+ return await response.json()
27
+
28
+ async def _ensure_valid_token(self):
29
+ if self.access_token:
30
+ decoded_token = jwt.decode(
31
+ self.access_token, options={"verify_signature": False}
32
+ )
33
+ exp = datetime.fromtimestamp(decoded_token.get("exp"))
34
+ if exp < datetime.now():
35
+ self.access_token = None
36
+
37
+ if not self.access_token:
38
+ token_data = await self._refresh()
39
+ self.access_token = token_data["access_token"]
40
+
41
+ self.headers.update({"Authorization": f"Bearer {self.access_token}"})
42
+
43
+ async def __aenter__(self):
44
+ await self._ensure_valid_token()
45
+ return self
46
+
47
+ async def __aexit__(self, exc_type, exc_value, traceback):
48
+ await self.close()
49
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: usso
3
- Version: 0.21.2
3
+ Version: 0.23.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>
@@ -1,5 +1,7 @@
1
1
  usso/__init__.py,sha256=RN0E6HI9kHedvDxheDPVWiAmh-169IrhXfvq_sHphQc,65
2
2
  usso/api.py,sha256=MnUg-DcrmMXW1CxEcRlJQhIYl5J1WKvjqHQOz7jsrwE,5137
3
+ usso/async_api.py,sha256=aXgYKMT1h4HV8szCqMCnoMmDya-IO6mVROpK8cSB5O4,5582
4
+ usso/async_session.py,sha256=zFqK2H5QGijrHWybR_IWLtgdtcWeR9PUtCRsqzKMLWw,1578
3
5
  usso/b64tools.py,sha256=7lsz_Rn6PITkKWfjcebmewdhvFs2A7H4t_WE33MgZ0Y,626
4
6
  usso/core.py,sha256=bJPKxJLcYe5zWoiNM_n9ajHSP5bARN8u9tsRQ6fLnyA,3998
5
7
  usso/exceptions.py,sha256=hawOAuVbvQtjgRfwp1KFZ4SmV7fh720y5Gom9JVA8W8,504
@@ -7,9 +9,9 @@ usso/package_data.dat,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
9
  usso/session.py,sha256=S3dFXzar0Pcwxj5TGqadKwGdmzmzgp4H2W3brfOwJ6A,1184
8
10
  usso/fastapi/__init__.py,sha256=TRTDVJo8bwZQDAuCQFhh-g1XbIspf6TdFYXGAO5cgAU,130
9
11
  usso/fastapi/integration.py,sha256=-zW3dwyvnVx59DgtV8JKL6YwG_ksFY2dOMwZAtwmEmY,1816
10
- usso-0.21.2.dist-info/LICENSE.txt,sha256=ceC9ZJOV9H6CtQDcYmHOS46NA3dHJ_WD4J9blH513pc,1081
11
- usso-0.21.2.dist-info/METADATA,sha256=8SFgZ3XwcJmXcqVeUop5ziHwRivUMn2ilTQjWgwFqR4,4234
12
- usso-0.21.2.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
13
- usso-0.21.2.dist-info/entry_points.txt,sha256=4Zgpm5ELaAWPf0jPGJFz1_X69H7un8ycT3WdGoJ0Vvk,35
14
- usso-0.21.2.dist-info/top_level.txt,sha256=g9Jf6h1Oyidh0vPiFni7UHInTJjSvu6cUalpLTIvthg,5
15
- usso-0.21.2.dist-info/RECORD,,
12
+ usso-0.23.0.dist-info/LICENSE.txt,sha256=ceC9ZJOV9H6CtQDcYmHOS46NA3dHJ_WD4J9blH513pc,1081
13
+ usso-0.23.0.dist-info/METADATA,sha256=8V-92GyH9HNte3lTORWyH8G2TmVhbK9ksP5LpRcrmzM,4234
14
+ usso-0.23.0.dist-info/WHEEL,sha256=cpQTJ5IWu9CdaPViMhC9YzF8gZuS5-vlfoFihTBC86A,91
15
+ usso-0.23.0.dist-info/entry_points.txt,sha256=4Zgpm5ELaAWPf0jPGJFz1_X69H7un8ycT3WdGoJ0Vvk,35
16
+ usso-0.23.0.dist-info/top_level.txt,sha256=g9Jf6h1Oyidh0vPiFni7UHInTJjSvu6cUalpLTIvthg,5
17
+ usso-0.23.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.43.0)
2
+ Generator: setuptools (70.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5