usso 0.12.0__tar.gz → 0.13.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: usso
3
- Version: 0.12.0
3
+ Version: 0.13.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>
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "usso"
7
- version = "0.12.0"
7
+ version = "0.13.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.8"
@@ -0,0 +1 @@
1
+ from .core import UserData, user_data_from_token
@@ -0,0 +1,97 @@
1
+ import os
2
+ import logging
3
+ import uuid
4
+ from functools import lru_cache
5
+ from typing import Optional, Tuple
6
+
7
+ import jwt
8
+ from pydantic import BaseModel
9
+
10
+ from . import b64tools
11
+ from .exceptions import USSOException
12
+
13
+ logger = logging.getLogger("usso")
14
+
15
+
16
+ class UserData(BaseModel):
17
+ user_id: str
18
+ email: str | None = None
19
+ phone: str | None = None
20
+ authentication_method: str | None = None
21
+ is_active: bool = False
22
+ jti: str
23
+ data: dict | None = None
24
+ token: str | None = None
25
+
26
+ @property
27
+ def uid(self) -> uuid.UUID:
28
+ user_id = self.user_id
29
+
30
+ if user_id.startswith("u_"):
31
+ user_id = user_id[2:]
32
+ if 22 <= len(user_id) <= 24:
33
+ user_id = b64tools.b64_decode_uuid(user_id)
34
+
35
+ return uuid.UUID(user_id)
36
+
37
+ @property
38
+ def b64id(self) -> uuid.UUID:
39
+ return b64tools.b64_encode_uuid_strip(self.uid)
40
+
41
+
42
+ def get_authorization_scheme_param(
43
+ authorization_header_value: Optional[str],
44
+ ) -> Tuple[str, str]:
45
+ if not authorization_header_value:
46
+ return "", ""
47
+ scheme, _, param = authorization_header_value.partition(" ")
48
+ return scheme, param
49
+
50
+
51
+ def user_data_from_token(token: str, **kwargs) -> UserData | None:
52
+ """Return the user associated with a token value."""
53
+ try:
54
+ header = jwt.get_unverified_header(token)
55
+ jwks_url = header["jwk_url"]
56
+ assert os.getenv("USSO_JWKS_URL") == jwks_url
57
+ jwks_client = get_jwks_keys(jwks_url)
58
+ # , headers=optional_custom_headers)
59
+ signing_key = jwks_client.get_signing_key_from_jwt(token)
60
+ decoded = jwt.decode(
61
+ token,
62
+ signing_key.key,
63
+ algorithms=["RS256"],
64
+ )
65
+ decoded["token"] = token
66
+
67
+ except jwt.exceptions.ExpiredSignatureError:
68
+ if kwargs.get("raise_exception"):
69
+ raise USSOException(status_code=401, error="expired_signature")
70
+ return None
71
+ except jwt.exceptions.InvalidSignatureError:
72
+ if kwargs.get("raise_exception"):
73
+ raise USSOException(status_code=401, error="invalid_signature")
74
+ return None
75
+ except jwt.exceptions.InvalidTokenError:
76
+ if kwargs.get("raise_exception"):
77
+ raise USSOException(
78
+ status_code=401,
79
+ error="invalid_token",
80
+ )
81
+ return None
82
+ except Exception as e:
83
+ if kwargs.get("raise_exception"):
84
+ raise USSOException(
85
+ status_code=401,
86
+ error="error",
87
+ message=str(e),
88
+ )
89
+ logger.error(e)
90
+ return None
91
+
92
+ return UserData(**decoded)
93
+
94
+
95
+ @lru_cache
96
+ def get_jwks_keys(jwks_url):
97
+ return jwt.PyJWKClient(jwks_url)
@@ -0,0 +1 @@
1
+ from .integration import jwt_access_security
@@ -0,0 +1,53 @@
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, get_authorization_scheme_param, user_data_from_token
7
+ from usso.exceptions import USSOException
8
+
9
+ logger = logging.getLogger("usso")
10
+
11
+
12
+ async def jwt_access_security(request: Request) -> UserData | None:
13
+ """Return the user associated with a token value."""
14
+ kwargs = {}
15
+ authorization = request.headers.get("Authorization")
16
+ if authorization:
17
+ scheme, _, credentials = get_authorization_scheme_param(authorization)
18
+ if scheme.lower() == "bearer":
19
+ token = credentials
20
+ return user_data_from_token(token, **kwargs)
21
+
22
+ cookie_token = request.cookies.get("access_token")
23
+ if cookie_token:
24
+ return user_data_from_token(cookie_token, **kwargs)
25
+
26
+ if kwargs.get("raise_exception", True):
27
+ raise USSOException(
28
+ status_code=HTTP_401_UNAUTHORIZED,
29
+ error="unauthorized",
30
+ )
31
+ return None
32
+
33
+
34
+ async def jwt_access_security_ws(websocket: WebSocket) -> UserData | None:
35
+ """Return the user associated with a token value."""
36
+ kwargs = {}
37
+ authorization = websocket.headers.get("Authorization")
38
+ if authorization:
39
+ scheme, _, credentials = get_authorization_scheme_param(authorization)
40
+ if scheme.lower() == "bearer":
41
+ token = credentials
42
+ return user_data_from_token(token, **kwargs)
43
+
44
+ cookie_token = websocket.cookies.get("access_token")
45
+ if cookie_token:
46
+ return user_data_from_token(cookie_token, **kwargs)
47
+
48
+ if kwargs.get("raise_exception", True):
49
+ raise USSOException(
50
+ status_code=HTTP_401_UNAUTHORIZED,
51
+ error="unauthorized",
52
+ )
53
+ return None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: usso
3
- Version: 0.12.0
3
+ Version: 0.13.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 +0,0 @@
1
- from .core import UserData
@@ -1,48 +0,0 @@
1
- import uuid
2
- from functools import lru_cache
3
- from typing import Optional, Tuple
4
-
5
- import jwt
6
- from pydantic import BaseModel
7
-
8
- from . import b64tools
9
-
10
-
11
- class UserData(BaseModel):
12
- user_id: str
13
- email: str | None = None
14
- phone: str | None = None
15
- authentication_method: str | None = None
16
- is_active: bool = False
17
- jti: str
18
- data: dict | None = None
19
- token: str | None = None
20
-
21
- @property
22
- def uid(self) -> uuid.UUID:
23
- user_id = self.user_id
24
-
25
- if user_id.startswith("u_"):
26
- user_id = user_id[2:]
27
- if 22 <= len(user_id) <= 24:
28
- user_id = b64tools.b64_decode_uuid(user_id)
29
-
30
- return uuid.UUID(user_id)
31
-
32
- @property
33
- def b64id(self) -> uuid.UUID:
34
- return b64tools.b64_encode_uuid_strip(self.uid)
35
-
36
-
37
- def get_authorization_scheme_param(
38
- authorization_header_value: Optional[str],
39
- ) -> Tuple[str, str]:
40
- if not authorization_header_value:
41
- return "", ""
42
- scheme, _, param = authorization_header_value.partition(" ")
43
- return scheme, param
44
-
45
-
46
- @lru_cache
47
- def get_jwks_keys(jwks_url):
48
- return jwt.PyJWKClient(jwks_url)
@@ -1 +0,0 @@
1
- from .integration import jwt_access_security, user_data_from_token
@@ -1,101 +0,0 @@
1
- import logging
2
-
3
- import jwt
4
- from fastapi import Request, WebSocket
5
- from starlette.status import HTTP_401_UNAUTHORIZED
6
-
7
- from usso.core import UserData, get_authorization_scheme_param, get_jwks_keys
8
- from usso.exceptions import USSOException
9
-
10
- logger = logging.getLogger("usso")
11
-
12
-
13
- async def user_data_from_token(token: str, **kwargs) -> UserData | None:
14
- """Return the user associated with a token value."""
15
- try:
16
- header = jwt.get_unverified_header(token)
17
- jwks_url = header["jwk_url"]
18
- jwks_client = get_jwks_keys(jwks_url)
19
- # , headers=optional_custom_headers)
20
- signing_key = jwks_client.get_signing_key_from_jwt(token)
21
- decoded = jwt.decode(
22
- token,
23
- signing_key.key,
24
- algorithms=["RS256"],
25
- )
26
- decoded["token"] = token
27
-
28
- except jwt.exceptions.ExpiredSignatureError:
29
- if kwargs.get("raise_exception"):
30
- raise USSOException(
31
- status_code=HTTP_401_UNAUTHORIZED, error="expired_signature"
32
- )
33
- return None
34
- except jwt.exceptions.InvalidSignatureError:
35
- if kwargs.get("raise_exception"):
36
- raise USSOException(
37
- status_code=HTTP_401_UNAUTHORIZED, error="invalid_signature"
38
- )
39
- return None
40
- except jwt.exceptions.InvalidTokenError:
41
- if kwargs.get("raise_exception"):
42
- raise USSOException(
43
- status_code=HTTP_401_UNAUTHORIZED,
44
- error="invalid_token",
45
- )
46
- return None
47
- except Exception as e:
48
- if kwargs.get("raise_exception"):
49
- raise USSOException(
50
- status_code=HTTP_401_UNAUTHORIZED,
51
- error="error",
52
- message=str(e),
53
- )
54
- logger.error(e)
55
- return None
56
-
57
- return UserData(**decoded)
58
-
59
-
60
- async def jwt_access_security(request: Request) -> UserData | None:
61
- """Return the user associated with a token value."""
62
- kwargs = {}
63
- authorization = request.headers.get("Authorization")
64
- if authorization:
65
- scheme, _, credentials = get_authorization_scheme_param(authorization)
66
- if scheme.lower() == "bearer":
67
- token = credentials
68
- return await user_data_from_token(token, **kwargs)
69
-
70
- cookie_token = request.cookies.get("access_token")
71
- if cookie_token:
72
- return await user_data_from_token(cookie_token, **kwargs)
73
-
74
- if kwargs.get("raise_exception", True):
75
- raise USSOException(
76
- status_code=HTTP_401_UNAUTHORIZED,
77
- error="unauthorized",
78
- )
79
- return None
80
-
81
-
82
- async def jwt_access_security_ws(websocket: WebSocket) -> UserData | None:
83
- """Return the user associated with a token value."""
84
- kwargs = {}
85
- authorization = websocket.headers.get("Authorization")
86
- if authorization:
87
- scheme, _, credentials = get_authorization_scheme_param(authorization)
88
- if scheme.lower() == "bearer":
89
- token = credentials
90
- return await user_data_from_token(token, **kwargs)
91
-
92
- cookie_token = websocket.cookies.get("access_token")
93
- if cookie_token:
94
- return await user_data_from_token(cookie_token, **kwargs)
95
-
96
- if kwargs.get("raise_exception", True):
97
- raise USSOException(
98
- status_code=HTTP_401_UNAUTHORIZED,
99
- error="unauthorized",
100
- )
101
- return None
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes