usso 0.27.22__py3-none-any.whl → 0.28.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/__init__.py +23 -2
- usso/auth/__init__.py +9 -0
- usso/auth/api_key.py +43 -0
- usso/auth/client.py +85 -0
- usso/auth/config.py +115 -0
- usso/exceptions.py +13 -0
- usso/integrations/django/__init__.py +3 -0
- usso/{django → integrations/django}/middleware.py +37 -29
- usso/integrations/fastapi/__init__.py +3 -0
- usso/integrations/fastapi/dependency.py +95 -0
- usso/models/user.py +119 -0
- usso/session/async_session.py +12 -8
- usso/session/base_session.py +26 -45
- usso/session/session.py +8 -34
- usso/utils/method_utils.py +12 -0
- usso/utils/string_utils.py +7 -0
- usso-0.28.0.dist-info/METADATA +172 -0
- usso-0.28.0.dist-info/RECORD +24 -0
- {usso-0.27.22.dist-info → usso-0.28.0.dist-info}/WHEEL +1 -1
- usso/b64tools.py +0 -20
- usso/client/__init__.py +0 -4
- usso/client/api.py +0 -174
- usso/client/async_api.py +0 -159
- usso/core.py +0 -160
- usso/fastapi/__init__.py +0 -7
- usso/fastapi/integration.py +0 -88
- usso/schemas.py +0 -67
- usso-0.27.22.dist-info/METADATA +0 -110
- usso-0.27.22.dist-info/RECORD +0 -22
- /usso/{django → utils}/__init__.py +0 -0
- {usso-0.27.22.dist-info → usso-0.28.0.dist-info}/entry_points.txt +0 -0
- {usso-0.27.22.dist-info → usso-0.28.0.dist-info/licenses}/LICENSE.txt +0 -0
- {usso-0.27.22.dist-info → usso-0.28.0.dist-info}/top_level.txt +0 -0
usso/client/async_api.py
DELETED
@@ -1,159 +0,0 @@
|
|
1
|
-
import logging
|
2
|
-
|
3
|
-
import httpx
|
4
|
-
from singleton import Singleton
|
5
|
-
|
6
|
-
from usso.core import UserData, Usso
|
7
|
-
|
8
|
-
|
9
|
-
class AsyncUssoAPI(metaclass=Singleton):
|
10
|
-
def __init__(
|
11
|
-
self,
|
12
|
-
url: str = "https://api.usso.io",
|
13
|
-
api_key: str = None,
|
14
|
-
refresh_token: str = None,
|
15
|
-
):
|
16
|
-
if url and not url.startswith("http"):
|
17
|
-
url = f"https://{url}"
|
18
|
-
url = url.rstrip("/")
|
19
|
-
self.url = url
|
20
|
-
assert (
|
21
|
-
api_key or refresh_token
|
22
|
-
), "Either api_key or refresh_token must be provided"
|
23
|
-
self.api_key = api_key
|
24
|
-
self.refresh_token = refresh_token
|
25
|
-
self.access_token = None
|
26
|
-
|
27
|
-
async def _refresh(self, **kwargs):
|
28
|
-
if not self.refresh_token:
|
29
|
-
return
|
30
|
-
|
31
|
-
url = f"{self.url}/auth/refresh"
|
32
|
-
headers = {
|
33
|
-
"Authorization": f"Bearer {self.refresh_token}",
|
34
|
-
"Content-Type": "application/json",
|
35
|
-
}
|
36
|
-
|
37
|
-
async with httpx.AsyncClient() as client:
|
38
|
-
resp = await client.post(url, headers=headers)
|
39
|
-
if kwargs.get("raise_exception", True):
|
40
|
-
resp.raise_for_status()
|
41
|
-
self.access_token = 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
|
-
return bool(user_data)
|
51
|
-
|
52
|
-
async def _request(
|
53
|
-
self,
|
54
|
-
method="get",
|
55
|
-
endpoint: str = "",
|
56
|
-
data: dict = None,
|
57
|
-
**kwargs,
|
58
|
-
) -> dict:
|
59
|
-
url = f"{self.url}/{endpoint}"
|
60
|
-
headers = {"Content-Type": "application/json"}
|
61
|
-
if self.api_key:
|
62
|
-
headers["x-api-key"] = self.api_key
|
63
|
-
elif self.refresh_token:
|
64
|
-
if not self.access_token:
|
65
|
-
await self._refresh()
|
66
|
-
headers["Authorization"] = f"Bearer {self.access_token}"
|
67
|
-
|
68
|
-
async with httpx.AsyncClient() as client:
|
69
|
-
try:
|
70
|
-
resp = await client.request(
|
71
|
-
method,
|
72
|
-
url,
|
73
|
-
headers=headers,
|
74
|
-
json=data,
|
75
|
-
)
|
76
|
-
resp.raise_for_status()
|
77
|
-
return resp.json()
|
78
|
-
except httpx.HTTPStatusError as e:
|
79
|
-
logging.error(f"HTTP error: {e.response.status_code} {e.response.text}")
|
80
|
-
raise e
|
81
|
-
except Exception as e:
|
82
|
-
logging.error(f"Unexpected error: {e}")
|
83
|
-
raise e
|
84
|
-
|
85
|
-
async def get_users(self, **kwargs) -> list[UserData]:
|
86
|
-
users_dict = await self._request(endpoint="website/users", **kwargs)
|
87
|
-
return [UserData(user_id=user.get("uid"), **user) for user in users_dict]
|
88
|
-
|
89
|
-
async def get_user(self, user_id: str, **kwargs) -> UserData:
|
90
|
-
user_dict = await self._request(endpoint=f"website/users/{user_id}", **kwargs)
|
91
|
-
return UserData(user_id=user_dict.get("uid"), **user_dict)
|
92
|
-
|
93
|
-
async def get_user_by_credentials(self, credentials: dict, **kwargs) -> UserData:
|
94
|
-
user_dict = await self._request(
|
95
|
-
endpoint="website/users/credentials", data=credentials, **kwargs
|
96
|
-
)
|
97
|
-
return UserData(user_id=user_dict.get("uid"), **user_dict)
|
98
|
-
|
99
|
-
async def create_user(self, user_data: dict, **kwargs) -> UserData:
|
100
|
-
user_dict = await self._request(
|
101
|
-
method="post", endpoint="website/users", data=user_data, **kwargs
|
102
|
-
)
|
103
|
-
return UserData(user_id=user_dict.get("uid"), **user_dict)
|
104
|
-
|
105
|
-
async def create_user_credentials(
|
106
|
-
self, user_id: str, credentials: dict, **kwargs
|
107
|
-
) -> UserData:
|
108
|
-
user_dict = await self._request(
|
109
|
-
method="post",
|
110
|
-
endpoint=f"website/users/{user_id}/credentials",
|
111
|
-
data=credentials,
|
112
|
-
**kwargs,
|
113
|
-
)
|
114
|
-
return UserData(user_id=user_dict.get("uid"), **user_dict)
|
115
|
-
|
116
|
-
async def create_user_by_credentials(
|
117
|
-
self,
|
118
|
-
user_data: dict | None = None,
|
119
|
-
credentials: dict | None = None,
|
120
|
-
**kwargs,
|
121
|
-
) -> UserData:
|
122
|
-
user_data = user_data or {}
|
123
|
-
if credentials:
|
124
|
-
user_data["authenticators"] = [credentials]
|
125
|
-
user_dict = await self._request(
|
126
|
-
method="post", endpoint="website/users", data=credentials, **kwargs
|
127
|
-
)
|
128
|
-
return UserData(user_id=user_dict.get("uid"), **user_dict)
|
129
|
-
|
130
|
-
async def get_user_payload(self, user_id: str, **kwargs) -> dict:
|
131
|
-
return await self._request(
|
132
|
-
endpoint=f"website/users/{user_id}/payload", **kwargs
|
133
|
-
)
|
134
|
-
|
135
|
-
async def update_user_payload(
|
136
|
-
self,
|
137
|
-
user_id: str,
|
138
|
-
payload: dict,
|
139
|
-
**kwargs,
|
140
|
-
) -> dict:
|
141
|
-
return await self._request(
|
142
|
-
method="patch",
|
143
|
-
endpoint=f"website/users/{user_id}/payload",
|
144
|
-
data=payload,
|
145
|
-
**kwargs,
|
146
|
-
)
|
147
|
-
|
148
|
-
async def set_user_payload(
|
149
|
-
self,
|
150
|
-
user_id: str,
|
151
|
-
payload: dict,
|
152
|
-
**kwargs,
|
153
|
-
) -> dict:
|
154
|
-
return await self._request(
|
155
|
-
method="put",
|
156
|
-
endpoint=f"website/users/{user_id}/payload",
|
157
|
-
data=payload,
|
158
|
-
**kwargs,
|
159
|
-
)
|
usso/core.py
DELETED
@@ -1,160 +0,0 @@
|
|
1
|
-
import json
|
2
|
-
import logging
|
3
|
-
import os
|
4
|
-
from datetime import datetime, timedelta
|
5
|
-
from urllib.parse import urlparse
|
6
|
-
|
7
|
-
import cachetools.func
|
8
|
-
import httpx
|
9
|
-
import jwt
|
10
|
-
|
11
|
-
from .exceptions import USSOException
|
12
|
-
from .schemas import JWTConfig, UserData
|
13
|
-
|
14
|
-
logger = logging.getLogger("usso")
|
15
|
-
|
16
|
-
|
17
|
-
def get_authorization_scheme_param(
|
18
|
-
authorization_header_value: str | None,
|
19
|
-
) -> tuple[str, str]:
|
20
|
-
if not authorization_header_value:
|
21
|
-
return "", ""
|
22
|
-
scheme, _, param = authorization_header_value.partition(" ")
|
23
|
-
return scheme, param
|
24
|
-
|
25
|
-
|
26
|
-
def decode_token(key, token: str, algorithms=["RS256"], **kwargs) -> dict:
|
27
|
-
"""Decode a JWT token."""
|
28
|
-
try:
|
29
|
-
decoded = jwt.decode(token, key, algorithms=algorithms)
|
30
|
-
decoded.update({"data": decoded, "token": token})
|
31
|
-
return UserData(**decoded)
|
32
|
-
except jwt.ExpiredSignatureError:
|
33
|
-
_handle_exception("expired_signature", **kwargs)
|
34
|
-
except jwt.InvalidSignatureError:
|
35
|
-
_handle_exception("invalid_signature", **kwargs)
|
36
|
-
except jwt.InvalidAlgorithmError:
|
37
|
-
_handle_exception("invalid_algorithm", **kwargs)
|
38
|
-
except jwt.InvalidIssuedAtError:
|
39
|
-
_handle_exception("invalid_issued_at", **kwargs)
|
40
|
-
except jwt.InvalidTokenError:
|
41
|
-
_handle_exception("invalid_token", **kwargs)
|
42
|
-
except jwt.InvalidKeyError:
|
43
|
-
_handle_exception("invalid_key", **kwargs)
|
44
|
-
except Exception as e:
|
45
|
-
_handle_exception("error", message=str(e), **kwargs)
|
46
|
-
|
47
|
-
|
48
|
-
def _handle_exception(error_type: str, **kwargs):
|
49
|
-
"""Handle JWT-related exceptions."""
|
50
|
-
if kwargs.get("raise_exception", True):
|
51
|
-
raise USSOException(
|
52
|
-
status_code=401, error=error_type, message=kwargs.get("message")
|
53
|
-
)
|
54
|
-
logger.error(kwargs.get("message") or error_type)
|
55
|
-
|
56
|
-
|
57
|
-
def is_expired(token: str, **kwargs) -> bool:
|
58
|
-
now = datetime.now()
|
59
|
-
decoded_token: dict = jwt.decode(token, options={"verify_signature": False})
|
60
|
-
exp = decoded_token.get("exp", (now + timedelta(days=1)).timestamp())
|
61
|
-
exp = datetime.fromtimestamp(exp)
|
62
|
-
return exp < now
|
63
|
-
|
64
|
-
|
65
|
-
@cachetools.func.ttl_cache(maxsize=128, ttl=10 * 60)
|
66
|
-
def get_jwk_keys(jwk_url: str) -> jwt.PyJWKClient:
|
67
|
-
return jwt.PyJWKClient(jwk_url, headers={"User-Agent": "usso-python"})
|
68
|
-
|
69
|
-
|
70
|
-
def decode_token_with_jwk(jwk_url: str, token: str, **kwargs) -> UserData | None:
|
71
|
-
"""Return the user associated with a token value."""
|
72
|
-
try:
|
73
|
-
jwk_client = get_jwk_keys(jwk_url)
|
74
|
-
signing_key = jwk_client.get_signing_key_from_jwt(token)
|
75
|
-
return decode_token(signing_key.key, token, **kwargs)
|
76
|
-
except Exception as e:
|
77
|
-
_handle_exception("error", message=str(e), **kwargs)
|
78
|
-
|
79
|
-
|
80
|
-
@cachetools.func.ttl_cache(maxsize=128, ttl=10 * 60)
|
81
|
-
def fetch_api_key_data(jwk_url: str, api_key: str):
|
82
|
-
try:
|
83
|
-
parsed = urlparse(jwk_url)
|
84
|
-
url = f"{parsed.scheme}://{parsed.netloc}/api_key/verify"
|
85
|
-
response = httpx.post(url, json={"api_key": api_key})
|
86
|
-
response.raise_for_status()
|
87
|
-
return UserData(**response.json())
|
88
|
-
except Exception as e:
|
89
|
-
_handle_exception("error", message=str(e))
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
class Usso:
|
94
|
-
def __init__(
|
95
|
-
self,
|
96
|
-
*,
|
97
|
-
jwt_config: (
|
98
|
-
str | dict | JWTConfig | list[str] | list[dict] | list[JWTConfig] | None
|
99
|
-
) = None,
|
100
|
-
jwk_url: str | None = None,
|
101
|
-
secret: str | None = None,
|
102
|
-
):
|
103
|
-
self.jwt_configs = self._initialize_configs(jwt_config, jwk_url, secret)
|
104
|
-
|
105
|
-
def _initialize_configs(
|
106
|
-
self,
|
107
|
-
jwt_config: (
|
108
|
-
str | dict | JWTConfig | list[str] | list[dict] | list[JWTConfig] | None
|
109
|
-
) = None,
|
110
|
-
jwk_url: str | None = None,
|
111
|
-
secret: str | None = None,
|
112
|
-
):
|
113
|
-
"""Initialize JWT configurations."""
|
114
|
-
if jwt_config is None:
|
115
|
-
jwt_config = os.getenv("USSO_JWT_CONFIG")
|
116
|
-
|
117
|
-
if jwt_config is None:
|
118
|
-
jwk_url = jwk_url or os.getenv("USSO_JWK_URL") or os.getenv("USSO_JWKS_URL")
|
119
|
-
secret = secret or os.getenv("USSO_SECRET")
|
120
|
-
if jwk_url:
|
121
|
-
return [JWTConfig(jwk_url=jwk_url)]
|
122
|
-
if secret:
|
123
|
-
return [JWTConfig(secret=secret)]
|
124
|
-
raise ValueError(
|
125
|
-
"Provide jwt_config, jwk_url, or secret, or set the appropriate environment variables."
|
126
|
-
)
|
127
|
-
|
128
|
-
if isinstance(jwt_config, (str, dict, JWTConfig)):
|
129
|
-
return [self._parse_config(jwt_config)]
|
130
|
-
if isinstance(jwt_config, list):
|
131
|
-
return [self._parse_config(config) for config in jwt_config]
|
132
|
-
raise ValueError("Invalid jwt_config format")
|
133
|
-
|
134
|
-
def _parse_config(self, config):
|
135
|
-
"""Parse a single JWT configuration."""
|
136
|
-
if isinstance(config, str):
|
137
|
-
config = json.loads(config)
|
138
|
-
if isinstance(config, dict):
|
139
|
-
return JWTConfig(**config)
|
140
|
-
return config
|
141
|
-
|
142
|
-
def user_data_from_token(self, token: str, **kwargs) -> UserData | None:
|
143
|
-
"""Return the user associated with a token value."""
|
144
|
-
exp = None
|
145
|
-
for jwk_config in self.jwt_configs:
|
146
|
-
try:
|
147
|
-
user_data = jwk_config.decode(token)
|
148
|
-
if user_data.token_type.lower() != kwargs.get("token_type", "access"):
|
149
|
-
_handle_exception("invalid_token_type", **kwargs)
|
150
|
-
return user_data
|
151
|
-
except USSOException as e:
|
152
|
-
exp = e
|
153
|
-
|
154
|
-
if kwargs.get("raise_exception", True):
|
155
|
-
if exp:
|
156
|
-
_handle_exception(exp.error, message=str(exp), **kwargs)
|
157
|
-
_handle_exception("unauthorized", **kwargs)
|
158
|
-
|
159
|
-
def user_data_from_api_key(self, api_key: str):
|
160
|
-
return fetch_api_key_data(self.jwt_configs[0].jwk_url, api_key)
|
usso/fastapi/__init__.py
DELETED
usso/fastapi/integration.py
DELETED
@@ -1,88 +0,0 @@
|
|
1
|
-
import logging
|
2
|
-
|
3
|
-
from fastapi import Request, WebSocket
|
4
|
-
from fastapi.responses import JSONResponse
|
5
|
-
from starlette.status import HTTP_401_UNAUTHORIZED
|
6
|
-
|
7
|
-
from usso.exceptions import USSOException
|
8
|
-
|
9
|
-
from ..core import UserData, Usso, get_authorization_scheme_param
|
10
|
-
|
11
|
-
logger = logging.getLogger("usso")
|
12
|
-
|
13
|
-
|
14
|
-
def get_request_token(request: Request | WebSocket) -> UserData | None:
|
15
|
-
authorization = request.headers.get("Authorization")
|
16
|
-
token = None
|
17
|
-
|
18
|
-
if authorization:
|
19
|
-
scheme, credentials = get_authorization_scheme_param(authorization)
|
20
|
-
if scheme.lower() == "bearer":
|
21
|
-
token = credentials
|
22
|
-
|
23
|
-
else:
|
24
|
-
cookie_token = request.cookies.get("usso_access_token")
|
25
|
-
if cookie_token:
|
26
|
-
token = cookie_token
|
27
|
-
|
28
|
-
return token
|
29
|
-
|
30
|
-
|
31
|
-
def jwt_access_security_None(request: Request, jwt_config=None) -> UserData | None:
|
32
|
-
"""Return the user associated with a token value."""
|
33
|
-
api_key = request.headers.get("x-api-key")
|
34
|
-
if api_key:
|
35
|
-
return Usso(jwt_config=jwt_config).user_data_from_api_key(api_key)
|
36
|
-
|
37
|
-
token = get_request_token(request)
|
38
|
-
if not token:
|
39
|
-
return None
|
40
|
-
return Usso(jwt_config=jwt_config).user_data_from_token(
|
41
|
-
token, raise_exception=False
|
42
|
-
)
|
43
|
-
|
44
|
-
|
45
|
-
def jwt_access_security(request: Request, jwt_config=None) -> UserData | None:
|
46
|
-
"""Return the user associated with a token value."""
|
47
|
-
api_key = request.headers.get("x-api-key")
|
48
|
-
if api_key:
|
49
|
-
return Usso(jwt_config=jwt_config).user_data_from_api_key(api_key)
|
50
|
-
|
51
|
-
token = get_request_token(request)
|
52
|
-
if not token:
|
53
|
-
raise USSOException(
|
54
|
-
status_code=HTTP_401_UNAUTHORIZED,
|
55
|
-
error="unauthorized",
|
56
|
-
message="No token provided",
|
57
|
-
)
|
58
|
-
|
59
|
-
return Usso(jwt_config=jwt_config).user_data_from_token(token)
|
60
|
-
|
61
|
-
|
62
|
-
def jwt_access_security_ws(websocket: WebSocket, jwt_config=None) -> UserData | None:
|
63
|
-
"""Return the user associated with a token value."""
|
64
|
-
api_key = websocket.headers.get("x-api-key")
|
65
|
-
if api_key:
|
66
|
-
return Usso(jwt_config=jwt_config).user_data_from_api_key(api_key)
|
67
|
-
|
68
|
-
token = get_request_token(websocket)
|
69
|
-
if not token:
|
70
|
-
raise USSOException(
|
71
|
-
status_code=HTTP_401_UNAUTHORIZED,
|
72
|
-
error="unauthorized",
|
73
|
-
message="No token provided",
|
74
|
-
)
|
75
|
-
|
76
|
-
return Usso(jwt_config=jwt_config).user_data_from_token(token)
|
77
|
-
|
78
|
-
|
79
|
-
async def usso_exception_handler(request: Request, exc: USSOException):
|
80
|
-
return JSONResponse(
|
81
|
-
status_code=exc.status_code,
|
82
|
-
content={"message": exc.message, "error": exc.error},
|
83
|
-
)
|
84
|
-
|
85
|
-
|
86
|
-
EXCEPTION_HANDLERS = {
|
87
|
-
USSOException: usso_exception_handler,
|
88
|
-
}
|
usso/schemas.py
DELETED
@@ -1,67 +0,0 @@
|
|
1
|
-
import uuid
|
2
|
-
|
3
|
-
import cachetools.func
|
4
|
-
from pydantic import BaseModel, model_validator
|
5
|
-
|
6
|
-
from . import b64tools
|
7
|
-
|
8
|
-
|
9
|
-
class UserData(BaseModel):
|
10
|
-
user_id: str
|
11
|
-
workspace_id: str | None = None
|
12
|
-
workspace_ids: list[str] = []
|
13
|
-
token_type: str = "access"
|
14
|
-
|
15
|
-
email: str | None = None
|
16
|
-
phone: str | None = None
|
17
|
-
username: str | None = None
|
18
|
-
|
19
|
-
authentication_method: str | None = None
|
20
|
-
is_active: bool = False
|
21
|
-
|
22
|
-
jti: str | None = None
|
23
|
-
data: dict | None = None
|
24
|
-
|
25
|
-
token: str | None = None
|
26
|
-
|
27
|
-
@property
|
28
|
-
def uid(self) -> uuid.UUID:
|
29
|
-
user_id = self.user_id
|
30
|
-
|
31
|
-
if user_id.startswith("u_"):
|
32
|
-
user_id = user_id[2:]
|
33
|
-
if 22 <= len(user_id) <= 24:
|
34
|
-
user_id = b64tools.b64_decode_uuid(user_id)
|
35
|
-
|
36
|
-
return uuid.UUID(user_id)
|
37
|
-
|
38
|
-
@property
|
39
|
-
def b64id(self) -> uuid.UUID:
|
40
|
-
return b64tools.b64_encode_uuid_strip(self.uid)
|
41
|
-
|
42
|
-
|
43
|
-
class JWTConfig(BaseModel):
|
44
|
-
"""Configuration for JWT processing."""
|
45
|
-
|
46
|
-
jwk_url: str | None = None
|
47
|
-
secret: str | None = None
|
48
|
-
algorithm: str = "RS256"
|
49
|
-
header: dict[str, str] = {"type": "Cookie", "name": "usso_access_token"}
|
50
|
-
|
51
|
-
def __hash__(self):
|
52
|
-
return hash(self.model_dump_json())
|
53
|
-
|
54
|
-
@model_validator(mode="before")
|
55
|
-
def validate_config(cls, data: dict):
|
56
|
-
if not data.get("jwk_url") and not data.get("secret"):
|
57
|
-
raise ValueError("Either jwk_url or secret must be provided")
|
58
|
-
return data
|
59
|
-
|
60
|
-
@cachetools.func.ttl_cache(maxsize=128, ttl=600)
|
61
|
-
def decode(self, token: str):
|
62
|
-
"""Decode a token using the configured method."""
|
63
|
-
from .core import decode_token, decode_token_with_jwk
|
64
|
-
|
65
|
-
if self.jwk_url:
|
66
|
-
return decode_token_with_jwk(self.jwk_url, token)
|
67
|
-
return decode_token(self.secret, token, algorithms=[self.algorithm])
|
usso-0.27.22.dist-info/METADATA
DELETED
@@ -1,110 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.2
|
2
|
-
Name: usso
|
3
|
-
Version: 0.27.22
|
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
|
-
Author-email: Mahdi Kiani <mahdikiany@gmail.com>
|
6
|
-
Maintainer-email: Mahdi Kiani <mahdikiany@gmail.com>
|
7
|
-
License: Copyright (c) 2016 The Python Packaging Authority (PyPA)
|
8
|
-
|
9
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
10
|
-
this software and associated documentation files (the "Software"), to deal in
|
11
|
-
the Software without restriction, including without limitation the rights to
|
12
|
-
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
13
|
-
of the Software, and to permit persons to whom the Software is furnished to do
|
14
|
-
so, subject to the following conditions:
|
15
|
-
|
16
|
-
The above copyright notice and this permission notice shall be included in all
|
17
|
-
copies or substantial portions of the Software.
|
18
|
-
|
19
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
20
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
21
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
22
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
23
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
24
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
25
|
-
SOFTWARE.
|
26
|
-
|
27
|
-
Project-URL: Homepage, https://github.com/ussoio/usso-python
|
28
|
-
Project-URL: Bug Reports, https://github.com/ussoio/usso-python/issues
|
29
|
-
Project-URL: Funding, https://github.com/ussoio/usso-python
|
30
|
-
Project-URL: Say Thanks!, https://saythanks.io/to/mahdikiani
|
31
|
-
Project-URL: Source, https://github.com/ussoio/usso-python
|
32
|
-
Keywords: usso,sso,authentication,security,fastapi,django
|
33
|
-
Classifier: Development Status :: 3 - Alpha
|
34
|
-
Classifier: Intended Audience :: Developers
|
35
|
-
Classifier: Topic :: Software Development :: Build Tools
|
36
|
-
Classifier: License :: OSI Approved :: MIT License
|
37
|
-
Classifier: Programming Language :: Python :: 3
|
38
|
-
Classifier: Programming Language :: Python :: 3.10
|
39
|
-
Classifier: Programming Language :: Python :: 3.11
|
40
|
-
Classifier: Programming Language :: Python :: 3.12
|
41
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
42
|
-
Requires-Python: >=3.9
|
43
|
-
Description-Content-Type: text/markdown
|
44
|
-
License-File: LICENSE.txt
|
45
|
-
Requires-Dist: pydantic>=2
|
46
|
-
Requires-Dist: pyjwt[crypto]
|
47
|
-
Requires-Dist: cachetools
|
48
|
-
Requires-Dist: singleton_package
|
49
|
-
Requires-Dist: json-advanced
|
50
|
-
Requires-Dist: httpx
|
51
|
-
Provides-Extra: fastapi
|
52
|
-
Requires-Dist: fastapi>=0.65.0; extra == "fastapi"
|
53
|
-
Requires-Dist: uvicorn[standard]>=0.13.0; extra == "fastapi"
|
54
|
-
Provides-Extra: django
|
55
|
-
Requires-Dist: Django>=3.2; extra == "django"
|
56
|
-
Provides-Extra: httpx
|
57
|
-
Requires-Dist: httpx; extra == "httpx"
|
58
|
-
Provides-Extra: dev
|
59
|
-
Requires-Dist: check-manifest; extra == "dev"
|
60
|
-
Provides-Extra: test
|
61
|
-
Requires-Dist: coverage; extra == "test"
|
62
|
-
Provides-Extra: all
|
63
|
-
Requires-Dist: fastapi; extra == "all"
|
64
|
-
Requires-Dist: uvicorn; extra == "all"
|
65
|
-
Requires-Dist: django; extra == "all"
|
66
|
-
Requires-Dist: httpx; extra == "all"
|
67
|
-
Requires-Dist: dev; extra == "all"
|
68
|
-
Requires-Dist: test; extra == "all"
|
69
|
-
|
70
|
-
# USSO-Client
|
71
|
-
|
72
|
-
The USSO-Client provides a universal single sign-on (SSO) integration for microservices, making it easy to add secure, scalable authentication across different frameworks. This client simplifies the process of connecting any microservice to the USSO service.
|
73
|
-
|
74
|
-
## Features
|
75
|
-
|
76
|
-
- **Core SSO Integration**: Use the USSO core client for basic SSO functionality across any Python application.
|
77
|
-
- **Framework-Specific Modules**:
|
78
|
-
- **FastAPI Integration**: Specialized support for FastAPI applications, enabling async authentication mechanisms tailored to FastAPI's event loop.
|
79
|
-
- *Django Integration* (Coming soon): Customizable Django authentication backend that integrates seamlessly with Django's user management and middleware architecture.
|
80
|
-
|
81
|
-
## Installation
|
82
|
-
|
83
|
-
Install the USSO client using pip:
|
84
|
-
|
85
|
-
```bash
|
86
|
-
pip install usso
|
87
|
-
```
|
88
|
-
|
89
|
-
To add framework-specific support, use the following commands:
|
90
|
-
|
91
|
-
For FastAPI:
|
92
|
-
|
93
|
-
```bash
|
94
|
-
pip install "usso[fastapi]"
|
95
|
-
```
|
96
|
-
|
97
|
-
For Django:
|
98
|
-
|
99
|
-
```bash
|
100
|
-
pip install "usso[django]"
|
101
|
-
```
|
102
|
-
|
103
|
-
## Quick Start
|
104
|
-
Follow the quick start guides in the documentation to integrate USSO in your application.
|
105
|
-
|
106
|
-
## Contributing
|
107
|
-
Contributions are welcome! See CONTRIBUTING.md for more details on how to get involved.
|
108
|
-
|
109
|
-
## License
|
110
|
-
Distributed under the MIT License. See LICENSE for more information.
|
usso-0.27.22.dist-info/RECORD
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
usso/__init__.py,sha256=NnOS_S1a-JKTOlGe1nw-kCL3m0y82mA2mDraus7BQ2o,120
|
2
|
-
usso/b64tools.py,sha256=HGQ0E59vzjrQo2-4jrcY03ebtTaYwTtCZ7KgJaEmxO0,610
|
3
|
-
usso/core.py,sha256=kZCdHQz-sIbmd9MUd4CVMZACtvP60rbpL1Gszy3tkzQ,5641
|
4
|
-
usso/exceptions.py,sha256=ogJsjdUK0HoZdQv5uCnzIVoG-bTTMHBqyvB4swAMsiE,653
|
5
|
-
usso/schemas.py,sha256=aK_UWZvqjZLz5r1yBIZX_nL2yPCNUjxpZ93AsV9mAes,1810
|
6
|
-
usso/client/__init__.py,sha256=ilGFrugI7bhGXVIcETdbRAye8S7k2mVjkEeziToVzSs,100
|
7
|
-
usso/client/api.py,sha256=-sluL8BMjcI1z8y8thZElCa63nxo25qlmKh3IzK3M6U,5124
|
8
|
-
usso/client/async_api.py,sha256=UMN84vmWvLgi-3zsNdhCOjf5zQQQFUJ-wKLztI_rU58,5186
|
9
|
-
usso/django/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
|
-
usso/django/middleware.py,sha256=EEEpHvMQ6QiWw2HY8zQ2Aec0RCATcLWsCKeyiPWJKio,3245
|
11
|
-
usso/fastapi/__init__.py,sha256=0EcdOzb4f3yu9nILIdGWnlyUz-0VaVX2az1e3f2BusI,201
|
12
|
-
usso/fastapi/integration.py,sha256=IonxxNj_B9sG2j672rIzE047qo972vk7ch4-eGENp3Q,2638
|
13
|
-
usso/session/__init__.py,sha256=tE4qWUdSI7iN_pywm47Mg8NKOTBa2nCNwCy3wCZWRmU,124
|
14
|
-
usso/session/async_session.py,sha256=bOWUeBpE2reDdK9zXrD4ZrZrRWlEOyyafWgPcLg2WLY,3638
|
15
|
-
usso/session/base_session.py,sha256=1Ad6LbX8IsCPTeEIopnYwybrkPkN5zgGUobv8VHcipA,3430
|
16
|
-
usso/session/session.py,sha256=T3v3lGvLxs4Yi-zZRzdKHLxEo2rtVewcLk-1V6Y44Jk,2569
|
17
|
-
usso-0.27.22.dist-info/LICENSE.txt,sha256=ceC9ZJOV9H6CtQDcYmHOS46NA3dHJ_WD4J9blH513pc,1081
|
18
|
-
usso-0.27.22.dist-info/METADATA,sha256=_di-fqd1GQaRLFMQHK2aVHv6xApGgeOEPlYYjqoI15E,4580
|
19
|
-
usso-0.27.22.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
20
|
-
usso-0.27.22.dist-info/entry_points.txt,sha256=4Zgpm5ELaAWPf0jPGJFz1_X69H7un8ycT3WdGoJ0Vvk,35
|
21
|
-
usso-0.27.22.dist-info/top_level.txt,sha256=g9Jf6h1Oyidh0vPiFni7UHInTJjSvu6cUalpLTIvthg,5
|
22
|
-
usso-0.27.22.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|