usso 0.27.22__py3-none-any.whl → 0.28.1__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 CHANGED
@@ -1,4 +1,25 @@
1
- from .core import UserData, Usso
1
+ """USSO - Universal Single Sign-On Client
2
+
3
+ A plug-and-play client for integrating universal single sign-on (SSO)
4
+ with Python frameworks, enabling secure and seamless authentication
5
+ across microservices.
6
+ """
7
+
8
+ from .auth import APIHeaderConfig, AuthConfig, HeaderConfig, UssoAuth
2
9
  from .exceptions import USSOException
10
+ from .models.user import UserData
11
+
12
+ __version__ = "0.28.0"
3
13
 
4
- __all__ = ["UserData", "Usso", "USSOException"]
14
+ __all__ = [
15
+ # Main client
16
+ "UssoAuth",
17
+ # Configuration
18
+ "AuthConfig",
19
+ "HeaderConfig",
20
+ "APIHeaderConfig",
21
+ # Models
22
+ "UserData",
23
+ # Exceptions
24
+ "USSOException",
25
+ ]
usso/auth/__init__.py ADDED
@@ -0,0 +1,9 @@
1
+ """USSO Authentication Module.
2
+
3
+ This module provides the core authentication functionality for USSO.
4
+ """
5
+
6
+ from .client import UssoAuth
7
+ from .config import APIHeaderConfig, AuthConfig, HeaderConfig
8
+
9
+ __all__ = ["UssoAuth", "AuthConfig", "HeaderConfig", "APIHeaderConfig"]
usso/auth/api_key.py ADDED
@@ -0,0 +1,43 @@
1
+ import logging
2
+ from urllib.parse import urlparse
3
+
4
+ import cachetools.func
5
+ import httpx
6
+
7
+ from ..exceptions import USSOException
8
+ from ..models.user import UserData
9
+
10
+ logger = logging.getLogger("usso")
11
+
12
+
13
+ def _handle_exception(error_type: str, **kwargs):
14
+ """Handle API key related exceptions."""
15
+ if kwargs.get("raise_exception", True):
16
+ raise USSOException(
17
+ status_code=401, error=error_type, message=kwargs.get("message")
18
+ )
19
+ logger.error(kwargs.get("message") or error_type)
20
+
21
+
22
+ @cachetools.func.ttl_cache(maxsize=128, ttl=10 * 60)
23
+ def fetch_api_key_data(jwk_url: str, api_key: str):
24
+ """Fetch user data using an API key.
25
+
26
+ Args:
27
+ jwk_url: The JWK URL to use for verification
28
+ api_key: The API key to verify
29
+
30
+ Returns:
31
+ UserData: The user data associated with the API key
32
+
33
+ Raises:
34
+ USSOException: If the API key is invalid or verification fails
35
+ """
36
+ try:
37
+ parsed = urlparse(jwk_url)
38
+ url = f"{parsed.scheme}://{parsed.netloc}/api_key/verify"
39
+ response = httpx.post(url, json={"api_key": api_key})
40
+ response.raise_for_status()
41
+ return UserData(**response.json())
42
+ except Exception as e:
43
+ _handle_exception("error", message=str(e))
usso/auth/client.py ADDED
@@ -0,0 +1,87 @@
1
+ import logging
2
+
3
+ import usso_jwt.exceptions
4
+ import usso_jwt.schemas
5
+
6
+ from ..exceptions import _handle_exception
7
+ from ..models.user import UserData
8
+ from .api_key import fetch_api_key_data
9
+ from .config import AuthConfig, AvailableJwtConfigs
10
+
11
+ logger = logging.getLogger("usso")
12
+
13
+
14
+ class UssoAuth:
15
+ """Main authentication client for USSO.
16
+
17
+ This client handles token validation, user data retrieval,
18
+ and API key verification.
19
+ """
20
+
21
+ def __init__(
22
+ self,
23
+ *,
24
+ jwt_config: AvailableJwtConfigs | None = None,
25
+ ):
26
+ """Initialize the USSO authentication client.
27
+
28
+ Args:
29
+ jwt_config: JWT configuration(s) to use for token validation
30
+ """
31
+ if jwt_config is None:
32
+ jwt_config = AuthConfig()
33
+ self.jwt_configs = AuthConfig.validate_jwt_configs(jwt_config)
34
+
35
+ def user_data_from_token(
36
+ self,
37
+ token: str,
38
+ *,
39
+ expected_acr: str | None = "access",
40
+ raise_exception: bool = True,
41
+ **kwargs,
42
+ ) -> UserData | None:
43
+ """Get user data from a JWT token.
44
+
45
+ Args:
46
+ token: The JWT token to validate
47
+ expected_acr: Expected authentication context reference
48
+ raise_exception: Whether to raise exception on error
49
+ **kwargs: Additional arguments to pass to token verification
50
+
51
+ Returns:
52
+ UserData if token is valid, None otherwise
53
+
54
+ Raises:
55
+ USSOException: If token is invalid and raise_exception is True
56
+ """
57
+ exp = None
58
+ for jwk_config in self.jwt_configs:
59
+ try:
60
+ jwt_obj = usso_jwt.schemas.JWT(
61
+ token=token, config=jwk_config, payload_class=UserData
62
+ )
63
+ if jwt_obj.verify(expected_acr=expected_acr, **kwargs):
64
+ return jwt_obj.payload
65
+ except usso_jwt.exceptions.JWTError as e:
66
+ exp = e
67
+
68
+ _handle_exception(
69
+ "Unauthorized",
70
+ message=str(exp) if exp else None,
71
+ raise_exception=raise_exception,
72
+ **kwargs,
73
+ )
74
+
75
+ def user_data_from_api_key(self, api_key: str) -> UserData:
76
+ """Get user data from an API key.
77
+
78
+ Args:
79
+ api_key: The API key to verify
80
+
81
+ Returns:
82
+ UserData: The user data associated with the API key
83
+
84
+ Raises:
85
+ USSOException: If the API key is invalid
86
+ """
87
+ return fetch_api_key_data(self.jwt_configs[0].jwk_url, api_key)
usso/auth/config.py ADDED
@@ -0,0 +1,115 @@
1
+ import json
2
+ from typing import Any, Literal, Union
3
+
4
+ import usso_jwt.config
5
+ from pydantic import BaseModel, model_validator
6
+
7
+ from ..models.user import UserData
8
+ from ..utils.string_utils import get_authorization_scheme_param
9
+
10
+
11
+ class HeaderConfig(BaseModel):
12
+ type: Literal["Authorization", "Cookie", "CustomHeader"] = "Cookie"
13
+ name: str = "usso_access_token"
14
+
15
+ @model_validator(mode="before")
16
+ def validate_header(cls, data: dict):
17
+ if data.get("type") == "Authorization" and not data.get("name"):
18
+ data["name"] = "Bearer"
19
+ elif data.get("type") == "Cookie":
20
+ data["name"] = data.get("name", "usso_access_token")
21
+ elif data.get("type") == "CustomHeader":
22
+ data["name"] = data.get("name", "x-usso-access-token")
23
+ return data
24
+
25
+ def __hash__(self):
26
+ return hash(self.model_dump_json())
27
+
28
+ def get_key(self, request) -> str | None:
29
+ headers: dict[str, Any] = getattr(request, "headers", {})
30
+ cookies: dict[str, str] = getattr(
31
+ request, "cookies", headers.get("Cookie", {})
32
+ )
33
+ if self.type == "CustomHeader":
34
+ return headers.get(self.name)
35
+ elif self.type == "Cookie":
36
+ return cookies.get(self.name)
37
+ elif self.type == "Authorization":
38
+ authorization = headers.get("Authorization")
39
+ if self.type == "Authorization":
40
+ authorization = headers.get("Authorization")
41
+ scheme, credentials = get_authorization_scheme_param(authorization)
42
+ if scheme.lower() == self.name.lower():
43
+ return credentials
44
+
45
+
46
+ class APIHeaderConfig(HeaderConfig):
47
+ verify_endpoint: str = "https://sso.usso.io/api_key/verify"
48
+
49
+
50
+ class AuthConfig(usso_jwt.config.JWTConfig):
51
+ """Configuration for JWT processing."""
52
+
53
+ api_key_header: APIHeaderConfig | None = APIHeaderConfig(
54
+ type="CustomHeader", name="x-api-key"
55
+ )
56
+ jwt_header: HeaderConfig | None = HeaderConfig()
57
+ static_api_keys: list[str] | None = None
58
+
59
+ def get_api_key(self, request) -> str | None:
60
+ if self.api_key_header:
61
+ return self.api_key_header.get_key(request)
62
+ return None
63
+
64
+ def get_jwt(self, request) -> str | None:
65
+ if self.jwt_header:
66
+ return self.jwt_header.get_key(request)
67
+ return None
68
+
69
+ def verify_token(
70
+ self, token: str, *, raise_exception: bool = True, **kwargs
71
+ ) -> bool:
72
+ from usso_jwt import exceptions as jwt_exceptions
73
+ from usso_jwt import schemas
74
+
75
+ try:
76
+ return schemas.JWT(
77
+ token=token,
78
+ config=self,
79
+ payload_class=UserData,
80
+ ).verify(**kwargs)
81
+ except jwt_exceptions.JWTError as e:
82
+ if raise_exception:
83
+ raise e
84
+ return False
85
+
86
+ @classmethod
87
+ def _parse_config(
88
+ cls, config: Union[str, dict, "AuthConfig"]
89
+ ) -> "AuthConfig":
90
+ """Parse a single JWT configuration."""
91
+ if isinstance(config, str):
92
+ config = json.loads(config)
93
+ if isinstance(config, dict):
94
+ return cls(**config)
95
+ if isinstance(config, cls):
96
+ return config
97
+ raise ValueError("Invalid JWT configuration")
98
+
99
+ @classmethod
100
+ def validate_jwt_configs(
101
+ cls,
102
+ jwt_config: Union[
103
+ str, dict, "AuthConfig", list[str], list[dict], list["AuthConfig"]
104
+ ],
105
+ ) -> list["AuthConfig"]:
106
+ if isinstance(jwt_config, (str, dict, cls)):
107
+ return [cls._parse_config(jwt_config)]
108
+ if isinstance(jwt_config, list):
109
+ return [cls._parse_config(config) for config in jwt_config]
110
+ raise ValueError("Invalid jwt_config format")
111
+
112
+
113
+ AvailableJwtConfigs = (
114
+ str | dict | AuthConfig | list[str] | list[dict] | list[AuthConfig]
115
+ )
usso/exceptions.py CHANGED
@@ -1,3 +1,7 @@
1
+ import logging
2
+
3
+ logger = logging.getLogger("usso")
4
+
1
5
  error_messages = {
2
6
  "invalid_signature": "Unauthorized. The JWT signature is invalid.",
3
7
  "invalid_token": "Unauthorized. The JWT is invalid or not provided.",
@@ -15,3 +19,12 @@ class USSOException(Exception):
15
19
  if message is None:
16
20
  self.message = error_messages[error]
17
21
  super().__init__(message)
22
+
23
+
24
+ def _handle_exception(error_type: str, **kwargs):
25
+ """Handle JWT-related exceptions."""
26
+ if kwargs.get("raise_exception", True):
27
+ raise USSOException(
28
+ status_code=401, error=error_type, message=kwargs.get("message")
29
+ )
30
+ logger.error(kwargs.get("message") or error_type)
@@ -0,0 +1,3 @@
1
+ from .middleware import USSOAuthenticationMiddleware
2
+
3
+ __all__ = ["USSOAuthenticationMiddleware"]
@@ -1,4 +1,5 @@
1
1
  import logging
2
+ from urllib.parse import urlparse
2
3
 
3
4
  from django.conf import settings
4
5
  from django.contrib.auth.models import User
@@ -6,17 +7,20 @@ from django.db.utils import IntegrityError
6
7
  from django.http import JsonResponse
7
8
  from django.http.request import HttpRequest
8
9
  from django.utils.deprecation import MiddlewareMixin
9
-
10
- from usso import UserData, Usso, USSOException
10
+ from usso import AuthConfig, UserData, UssoAuth, USSOException
11
11
 
12
12
  logger = logging.getLogger("usso")
13
13
 
14
14
 
15
15
  class USSOAuthenticationMiddleware(MiddlewareMixin):
16
+ @property
17
+ def jwt_config(self) -> AuthConfig:
18
+ return settings.USSO_JWT_CONFIG
16
19
 
17
20
  def process_request(self, request: HttpRequest):
18
21
  """
19
- Middleware to authenticate users by JWT token and create or return a user in the database.
22
+ Middleware to authenticate users by JWT token and create or
23
+ return a user in the database.
20
24
  """
21
25
  try:
22
26
  if hasattr(request, "user") and request.user.is_authenticated:
@@ -31,44 +35,48 @@ class USSOAuthenticationMiddleware(MiddlewareMixin):
31
35
  # Handle any errors raised by USSO authentication
32
36
  return JsonResponse({"error": str(e)}, status=401)
33
37
 
34
- def get_request_token(self, request: HttpRequest) -> str | None:
35
- authorization = request.headers.get("Authorization")
36
- if authorization:
37
- scheme, credentials = Usso(
38
- jwks_url=settings.USSO_JWK_URL
39
- ).get_authorization_scheme_param(authorization)
40
- if scheme.lower() == "bearer":
41
- return credentials # Bearer token
42
-
43
- return request.COOKIES.get("usso_access_token")
38
+ def get_request_jwt(self, request: HttpRequest) -> str | None:
39
+ return self.jwt_config.get_jwt(request)
44
40
 
45
- def jwt_access_security_none(self, request: HttpRequest) -> UserData | None:
41
+ def jwt_access_security_none(
42
+ self,
43
+ request: HttpRequest,
44
+ ) -> UserData | None:
46
45
  """Return the user associated with a token value."""
47
- token = self.get_request_token(request)
46
+ usso_auth = UssoAuth(jwt_config=self.jwt_config)
47
+ api_key = self.jwt_config.get_api_key(request)
48
+ if api_key:
49
+ return usso_auth.user_data_from_api_key(api_key)
50
+
51
+ token = self.get_request_jwt(request)
48
52
  if not token:
49
53
  return None
50
- return Usso(jwks_url=settings.USSO_JWK_URL).user_data_from_token(
51
- token, raise_exception=False
52
- )
54
+ return usso_auth.user_data_from_token(token, raise_exception=False)
53
55
 
54
56
  def jwt_access_security(self, request: HttpRequest) -> UserData | None:
55
57
  """Return the user associated with a token value."""
56
- token = self.get_request_token(request)
57
- if not token:
58
- raise USSOException(
59
- status_code=401,
60
- error="unauthorized",
61
- )
58
+ usso_auth = UssoAuth(jwt_config=self.jwt_config)
59
+ api_key = self.jwt_config.get_api_key(request)
60
+ if api_key:
61
+ return usso_auth.user_data_from_api_key(api_key)
62
62
 
63
- # Get user data from the token
64
- return Usso(jwks_url=settings.USSO_JWK_URL).user_data_from_token(token)
63
+ token = self.get_request_jwt(request)
64
+ if not token:
65
+ return None
66
+ return usso_auth.user_data_from_token(token, raise_exception=False)
65
67
 
66
68
  def get_or_create_user(self, user_data: UserData) -> User:
67
69
  """
68
- Check if a user exists by phone. If not, create a new user and return it.
70
+ Check if a user exists by phone. If not, create a new user
71
+ and return it.
69
72
  """
73
+ if self.jwt_config.jwk_url:
74
+ domain = urlparse(self.jwt_config.jwk_url).netloc
75
+ else:
76
+ domain = "example.com"
70
77
  phone = user_data.phone
71
- email = user_data.email or f"{user_data.user_id}@example.com" # Fallback email
78
+ email = user_data.email or f"{user_data.user_id}@{domain}"
79
+ # Fallback email
72
80
 
73
81
  try:
74
82
  # Try to get the user by phone
@@ -87,4 +95,4 @@ class USSOAuthenticationMiddleware(MiddlewareMixin):
87
95
 
88
96
  except IntegrityError as e:
89
97
  logger.error(f"Integrity error while creating user: {str(e)}")
90
- raise ValueError(f"Error while creating user: {str(e)}")
98
+ raise ValueError(f"Error while creating user: {str(e)}") from e
@@ -0,0 +1,8 @@
1
+ from .dependency import USSOAuthentication
2
+ from .handler import EXCEPTION_HANDLERS, usso_exception_handler
3
+
4
+ __all__ = [
5
+ "USSOAuthentication",
6
+ "EXCEPTION_HANDLERS",
7
+ "usso_exception_handler",
8
+ ]
@@ -0,0 +1,80 @@
1
+ import logging
2
+
3
+ from fastapi import Request, WebSocket
4
+
5
+ from ...auth import UssoAuth
6
+ from ...auth.config import AuthConfig, AvailableJwtConfigs
7
+ from ...exceptions import _handle_exception
8
+ from ...models.user import UserData
9
+ from ...utils.method_utils import instance_method
10
+
11
+ logger = logging.getLogger("usso")
12
+
13
+
14
+ class USSOAuthentication(UssoAuth):
15
+ def __init__(
16
+ self,
17
+ jwt_config: AvailableJwtConfigs | None = None,
18
+ raise_exception: bool = True,
19
+ ):
20
+ if jwt_config is None:
21
+ jwt_config = AuthConfig()
22
+
23
+ super().__init__(jwt_config=jwt_config)
24
+ self.raise_exception = raise_exception
25
+
26
+ def __call__(self, request: Request) -> UserData:
27
+ return self.usso_access_security(request)
28
+
29
+ @instance_method
30
+ def get_request_jwt(self, request: Request | WebSocket) -> str | None:
31
+ for jwt_config in self.jwt_configs:
32
+ token = jwt_config.get_jwt(request)
33
+ if token:
34
+ return token
35
+ return None
36
+
37
+ @instance_method
38
+ def get_request_api_key(self, request: Request | WebSocket) -> str | None:
39
+ for jwt_config in self.jwt_configs:
40
+ token = jwt_config.get_api_key(request)
41
+ if token:
42
+ return token
43
+ return None
44
+
45
+ # @instance_method
46
+ def usso_access_security(self, request: Request) -> UserData | None:
47
+ """Return the user associated with a token value."""
48
+ api_key = self.get_request_api_key(request)
49
+ if api_key:
50
+ return self.user_data_from_api_key(api_key)
51
+
52
+ token = self.get_request_jwt(request)
53
+ if token:
54
+ return self.user_data_from_token(
55
+ token, raise_exception=self.raise_exception
56
+ )
57
+
58
+ _handle_exception(
59
+ "Unauthorized",
60
+ message="No token provided",
61
+ raise_exception=self.raise_exception,
62
+ )
63
+
64
+ # @instance_method
65
+ def jwt_access_security_ws(self, websocket: WebSocket) -> UserData | None:
66
+ """Return the user associated with a token value."""
67
+ api_key = self.get_request_api_key(websocket)
68
+ if api_key:
69
+ return self.user_data_from_api_key(api_key)
70
+
71
+ token = self.get_request_jwt(websocket)
72
+ if token:
73
+ return self.user_data_from_token(
74
+ token, raise_exception=self.raise_exception
75
+ )
76
+ _handle_exception(
77
+ "Unauthorized",
78
+ message="No token provided",
79
+ raise_exception=self.raise_exception,
80
+ )
@@ -0,0 +1,16 @@
1
+ from fastapi import Request
2
+ from fastapi.responses import JSONResponse
3
+
4
+ from ...exceptions import USSOException
5
+
6
+
7
+ async def usso_exception_handler(request: Request, exc: USSOException):
8
+ return JSONResponse(
9
+ status_code=exc.status_code,
10
+ content={"message": exc.message, "error": exc.error},
11
+ )
12
+
13
+
14
+ EXCEPTION_HANDLERS = {
15
+ USSOException: usso_exception_handler,
16
+ }
usso/models/user.py ADDED
@@ -0,0 +1,119 @@
1
+ from collections.abc import Callable
2
+ from enum import StrEnum
3
+ from typing import Any, Literal
4
+
5
+ from pydantic import BaseModel
6
+
7
+
8
+ class TokenType(StrEnum):
9
+ ACCESS = "access"
10
+ REFRESH = "refresh"
11
+ SECURE_TOKEN = "secure"
12
+ ONE_TIME_TOKEN = "one_time"
13
+ TEMPORARY_TOKEN = "temporary"
14
+ MFA_TEMPORARY_TOKEN = "mfa_temporary"
15
+
16
+
17
+ class UserData(BaseModel):
18
+ jti: str | None = None
19
+ typ: TokenType | None = None
20
+ iss: str | None = None
21
+ aud: str | None = None
22
+ iat: int | None = None
23
+ nbf: int | None = None
24
+ exp: int | None = None
25
+ sub: str | None = None
26
+ tenant_id: str | None = None
27
+ workspace_id: str | None = None
28
+ roles: list[str] | None = None
29
+ scopes: list[str] | None = None
30
+ acr: str | None = None
31
+ signing_level: str | None = None
32
+
33
+ claims: dict | None = None
34
+
35
+ def __init__(
36
+ self,
37
+ *,
38
+ jti: str | None = None,
39
+ typ: TokenType | None = None,
40
+ iss: str | None = None,
41
+ aud: str | None = None,
42
+ iat: int | None = None,
43
+ nbf: int | None = None,
44
+ exp: int | None = None,
45
+ sub: str | None = None,
46
+ tenant_id: str | None = None,
47
+ workspace_id: str | None = None,
48
+ roles: list[str] | None = None,
49
+ scopes: list[str] | None = None,
50
+ acr: str | None = None,
51
+ signing_level: str | None = None,
52
+ **kwargs,
53
+ ):
54
+ super().__init__(
55
+ jti=jti,
56
+ typ=typ,
57
+ iss=iss,
58
+ aud=aud,
59
+ iat=iat,
60
+ nbf=nbf,
61
+ exp=exp,
62
+ sub=sub,
63
+ tenant_id=tenant_id,
64
+ workspace_id=workspace_id,
65
+ roles=roles,
66
+ scopes=scopes,
67
+ acr=acr,
68
+ signing_level=signing_level,
69
+ )
70
+ self.claims = self.model_dump() | kwargs
71
+
72
+ @property
73
+ def user_id(self) -> str:
74
+ if self.claims and "user_id" in self.claims:
75
+ return self.claims["user_id"]
76
+ return self.sub or ""
77
+
78
+ @property
79
+ def email(self) -> str:
80
+ if self.claims and "email" in self.claims:
81
+ return self.claims["email"]
82
+ return ""
83
+
84
+ @property
85
+ def phone(self) -> str:
86
+ if self.claims and "phone" in self.claims:
87
+ return self.claims["phone"]
88
+ return ""
89
+
90
+ def model_dump(
91
+ self,
92
+ *,
93
+ mode: Literal["json", "python"] | str = "python",
94
+ include=None,
95
+ exclude=None,
96
+ context: Any | None = None,
97
+ by_alias: bool | None = None,
98
+ exclude_unset: bool = False,
99
+ exclude_defaults: bool = False,
100
+ exclude_none: bool = True,
101
+ round_trip: bool = False,
102
+ warnings: bool | Literal["none", "warn", "error"] = True,
103
+ fallback: Callable[[Any], Any] | None = None,
104
+ serialize_as_any: bool = False,
105
+ ):
106
+ return super().model_dump(
107
+ mode=mode,
108
+ include=include,
109
+ exclude=exclude,
110
+ context=context,
111
+ by_alias=by_alias,
112
+ exclude_unset=exclude_unset,
113
+ exclude_defaults=exclude_defaults,
114
+ exclude_none=exclude_none,
115
+ round_trip=round_trip,
116
+ warnings=warnings,
117
+ fallback=fallback,
118
+ serialize_as_any=serialize_as_any,
119
+ )
@@ -37,7 +37,9 @@ class AsyncUssoSession(httpx.AsyncClient, BaseUssoSession):
37
37
  Helper function to prepare headers and parameters for refresh requests.
38
38
  """
39
39
  headers = (
40
- {"x-api-key": self.usso_admin_api_key} if self.usso_admin_api_key else {}
40
+ {"x-api-key": self.usso_admin_api_key}
41
+ if self.usso_admin_api_key
42
+ else {}
41
43
  )
42
44
  params = {"user_id": self.user_id} if self.user_id else {}
43
45
  return headers, params
@@ -51,13 +53,15 @@ class AsyncUssoSession(httpx.AsyncClient, BaseUssoSession):
51
53
  self.access_token = data.get("access_token")
52
54
  self._refresh_token = data.get("token", {}).get("refresh_token")
53
55
  if self.access_token:
54
- self.headers.update({"Authorization": f"Bearer {self.access_token}"})
56
+ self.headers.update({
57
+ "Authorization": f"Bearer {self.access_token}"
58
+ })
55
59
  return data
56
60
 
57
61
  def _refresh_sync(self) -> dict:
58
- assert (
59
- self.refresh_token or self.usso_admin_api_key
60
- ), "refresh_token or usso_api_key is required"
62
+ assert self.refresh_token or self.usso_admin_api_key, (
63
+ "refresh_token or usso_api_key is required"
64
+ )
61
65
 
62
66
  headers, params = self._prepare_refresh_request()
63
67
 
@@ -73,9 +77,9 @@ class AsyncUssoSession(httpx.AsyncClient, BaseUssoSession):
73
77
  return self._handle_refresh_response(response)
74
78
 
75
79
  async def _refresh(self) -> dict:
76
- assert (
77
- self.refresh_token or self.usso_admin_api_key
78
- ), "refresh_token or usso_api_key is required"
80
+ assert self.refresh_token or self.usso_admin_api_key, (
81
+ "refresh_token or usso_api_key is required"
82
+ )
79
83
 
80
84
  headers, params = self._prepare_refresh_request()
81
85