usso 0.14.0__tar.gz → 0.16.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.
- {usso-0.14.0/src/usso.egg-info → usso-0.16.0}/PKG-INFO +7 -6
- {usso-0.14.0 → usso-0.16.0}/pyproject.toml +8 -7
- usso-0.16.0/src/usso/api.py +102 -0
- usso-0.16.0/src/usso/core.py +100 -0
- usso-0.16.0/src/usso/fastapi/__init__.py +1 -0
- {usso-0.14.0 → usso-0.16.0}/src/usso/fastapi/integration.py +7 -7
- {usso-0.14.0 → usso-0.16.0/src/usso.egg-info}/PKG-INFO +7 -6
- {usso-0.14.0 → usso-0.16.0}/src/usso.egg-info/SOURCES.txt +1 -0
- {usso-0.14.0 → usso-0.16.0}/src/usso.egg-info/requires.txt +1 -0
- usso-0.14.0/src/usso/core.py +0 -97
- usso-0.14.0/src/usso/fastapi/__init__.py +0 -1
- {usso-0.14.0 → usso-0.16.0}/LICENSE.txt +0 -0
- {usso-0.14.0 → usso-0.16.0}/README.md +0 -0
- {usso-0.14.0 → usso-0.16.0}/setup.cfg +0 -0
- {usso-0.14.0 → usso-0.16.0}/src/usso/__init__.py +0 -0
- {usso-0.14.0 → usso-0.16.0}/src/usso/b64tools.py +0 -0
- {usso-0.14.0 → usso-0.16.0}/src/usso/exceptions.py +0 -0
- {usso-0.14.0 → usso-0.16.0}/src/usso/package_data.dat +0 -0
- {usso-0.14.0 → usso-0.16.0}/src/usso.egg-info/dependency_links.txt +0 -0
- {usso-0.14.0 → usso-0.16.0}/src/usso.egg-info/entry_points.txt +0 -0
- {usso-0.14.0 → usso-0.16.0}/src/usso.egg-info/top_level.txt +0 -0
- {usso-0.14.0 → usso-0.16.0}/tests/test_simple.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: usso
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.16.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>
|
@@ -24,11 +24,11 @@ License: Copyright (c) 2016 The Python Packaging Authority (PyPA)
|
|
24
24
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
25
25
|
SOFTWARE.
|
26
26
|
|
27
|
-
Project-URL: Homepage, https://github.com/
|
28
|
-
Project-URL: Bug Reports, https://github.com/
|
29
|
-
Project-URL: Funding, https://github.com/
|
30
|
-
Project-URL: Say Thanks!, https://github.com/
|
31
|
-
Project-URL: Source, https://github.com/
|
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://github.com/ussoio/usso-python
|
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
|
34
34
|
Classifier: Intended Audience :: Developers
|
@@ -48,6 +48,7 @@ Requires-Dist: peppercorn
|
|
48
48
|
Requires-Dist: pydantic>=1.8.2
|
49
49
|
Requires-Dist: requests>=2.26.0
|
50
50
|
Requires-Dist: pyjwt[crypto]
|
51
|
+
Requires-Dist: singleton_package
|
51
52
|
Provides-Extra: fastapi
|
52
53
|
Requires-Dist: fastapi>=0.65.0; extra == "fastapi"
|
53
54
|
Requires-Dist: uvicorn[standard]>=0.13.0; extra == "fastapi"
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "usso"
|
7
|
-
version = "0.
|
7
|
+
version = "0.16.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"
|
@@ -33,16 +33,17 @@ dependencies = [
|
|
33
33
|
"peppercorn", # Example main dependency
|
34
34
|
"pydantic>=1.8.2",
|
35
35
|
"requests>=2.26.0",
|
36
|
-
"pyjwt[crypto]"
|
36
|
+
"pyjwt[crypto]",
|
37
|
+
"singleton_package"
|
37
38
|
]
|
38
39
|
optional-dependencies = {"fastapi" = ["fastapi>=0.65.0", "uvicorn[standard]>=0.13.0"],"django" = ["Django>=3.2"],"dev" = ["check-manifest"],"test" = ["coverage"]}
|
39
40
|
|
40
41
|
[project.urls]
|
41
|
-
"Homepage" = "https://github.com/
|
42
|
-
"Bug Reports" = "https://github.com/
|
43
|
-
"Funding" = "https://github.com/
|
44
|
-
"Say Thanks!" = "https://github.com/
|
45
|
-
"Source" = "https://github.com/
|
42
|
+
"Homepage" = "https://github.com/ussoio/usso-python"
|
43
|
+
"Bug Reports" = "https://github.com/ussoio/usso-python/issues"
|
44
|
+
"Funding" = "https://github.com/ussoio/usso-python"
|
45
|
+
"Say Thanks!" = "https://github.com/ussoio/usso-python"
|
46
|
+
"Source" = "https://github.com/ussoio/usso-python"
|
46
47
|
|
47
48
|
[project.scripts]
|
48
49
|
usso = "usso:main"
|
@@ -0,0 +1,102 @@
|
|
1
|
+
import requests
|
2
|
+
from singleton import Singleton
|
3
|
+
|
4
|
+
from usso.core import Usso
|
5
|
+
|
6
|
+
|
7
|
+
class UssoAPI(metaclass=Singleton):
|
8
|
+
def __init__(
|
9
|
+
self,
|
10
|
+
url: str = "https://api.usso.io",
|
11
|
+
api_key: str = None,
|
12
|
+
refresh_token: str = None,
|
13
|
+
):
|
14
|
+
self.url = url
|
15
|
+
assert (
|
16
|
+
api_key or refresh_token
|
17
|
+
), "Either api_key or refresh_token must be provided"
|
18
|
+
self.api_key = api_key
|
19
|
+
self.refresh_token = refresh_token
|
20
|
+
self.access_token = None
|
21
|
+
|
22
|
+
def refresh(self):
|
23
|
+
if not self.refresh_token:
|
24
|
+
return
|
25
|
+
|
26
|
+
url = f"{self.url}/auth/refresh"
|
27
|
+
|
28
|
+
if self.refresh_token:
|
29
|
+
headers = {"Authorization": f"Bearer {self.refresh_token}"}
|
30
|
+
|
31
|
+
resp = requests.post(url, headers=headers)
|
32
|
+
self.access_token = resp.json().get("access_token")
|
33
|
+
|
34
|
+
def _access_valid(self):
|
35
|
+
if not self.access_token:
|
36
|
+
return False
|
37
|
+
|
38
|
+
user_data = Usso(
|
39
|
+
jwks_url=f"{self.url}/website/jwks.json?"
|
40
|
+
).user_data_from_token(self.access_token)
|
41
|
+
if user_data:
|
42
|
+
return True
|
43
|
+
return False
|
44
|
+
|
45
|
+
def _request(self, method="get", endpoint: str = "", data: dict = None):
|
46
|
+
url = f"{self.url}/{endpoint}"
|
47
|
+
headers = {}
|
48
|
+
if self.api_key:
|
49
|
+
headers["x-api-key"] = self.api_key
|
50
|
+
elif self.refresh_token:
|
51
|
+
if not self.access_token:
|
52
|
+
self.refresh()
|
53
|
+
headers["Authorization"] = f"Bearer {self.access_token}"
|
54
|
+
|
55
|
+
resp = requests.request(method, url, headers=headers, json=data)
|
56
|
+
return resp.json()
|
57
|
+
|
58
|
+
def get_users(self):
|
59
|
+
return self._request(endpoint="website/users/")
|
60
|
+
|
61
|
+
def get_user(self, user_id: str):
|
62
|
+
return self._request(endpoint=f"website/users/{user_id}/")
|
63
|
+
|
64
|
+
def get_user_credentials(self, user_id: str):
|
65
|
+
return self._request(endpoint=f"website/users/{user_id}/credentials/")
|
66
|
+
|
67
|
+
def get_user_by_credentials(self, credentials: dict):
|
68
|
+
return self._request(endpoint="website/users/credentials/", data=credentials)
|
69
|
+
|
70
|
+
def create_user(self, user_data: dict):
|
71
|
+
return self._request(method="post", endpoint="website/users/", data=user_data)
|
72
|
+
|
73
|
+
def create_user_credentials(self, user_id: str, credentials: dict):
|
74
|
+
return self._request(
|
75
|
+
method="post",
|
76
|
+
endpoint=f"website/users/{user_id}/credentials/",
|
77
|
+
data=credentials,
|
78
|
+
)
|
79
|
+
|
80
|
+
def create_user_by_credentials(
|
81
|
+
self, user_data: dict, credentials: dict | None = None
|
82
|
+
):
|
83
|
+
if credentials:
|
84
|
+
user_data["authenticators"] = [credentials]
|
85
|
+
return self._request(method="post", endpoint="website/users/", data=user_data)
|
86
|
+
|
87
|
+
def get_user_payload(self, user_id: str):
|
88
|
+
return self._request(endpoint=f"website/users/{user_id}/payload/")
|
89
|
+
|
90
|
+
def update_user_payload(self, user_id: str, payload: dict):
|
91
|
+
return self._request(
|
92
|
+
method="patch",
|
93
|
+
endpoint=f"website/users/{user_id}/payload/",
|
94
|
+
data=payload,
|
95
|
+
)
|
96
|
+
|
97
|
+
def set_user_payload(self, user_id: str, payload: dict):
|
98
|
+
return self._request(
|
99
|
+
method="put",
|
100
|
+
endpoint=f"website/users/{user_id}/payload/",
|
101
|
+
data=payload,
|
102
|
+
)
|
@@ -0,0 +1,100 @@
|
|
1
|
+
import os
|
2
|
+
import logging
|
3
|
+
import uuid
|
4
|
+
from functools import lru_cache
|
5
|
+
from typing import Optional, Tuple
|
6
|
+
from singleton import Singleton
|
7
|
+
|
8
|
+
import jwt
|
9
|
+
from pydantic import BaseModel
|
10
|
+
|
11
|
+
from . import b64tools
|
12
|
+
from .exceptions import USSOException
|
13
|
+
|
14
|
+
logger = logging.getLogger("usso")
|
15
|
+
|
16
|
+
|
17
|
+
class UserData(BaseModel):
|
18
|
+
user_id: str
|
19
|
+
email: str | None = None
|
20
|
+
phone: str | None = None
|
21
|
+
authentication_method: str | None = None
|
22
|
+
is_active: bool = False
|
23
|
+
jti: str
|
24
|
+
data: dict | None = None
|
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 Usso(metaclass=Singleton):
|
44
|
+
def __init__(self, jwks_url: str|None = None):
|
45
|
+
if jwks_url is None:
|
46
|
+
jwks_url = os.getenv("USSO_JWKS_URL")
|
47
|
+
self.jwks_url = jwks_url
|
48
|
+
|
49
|
+
@lru_cache
|
50
|
+
def get_jwks_keys(self):
|
51
|
+
return jwt.PyJWKClient(self.jwks_url)
|
52
|
+
|
53
|
+
def get_authorization_scheme_param(self,
|
54
|
+
authorization_header_value: Optional[str],
|
55
|
+
) -> Tuple[str, str]:
|
56
|
+
if not authorization_header_value:
|
57
|
+
return "", ""
|
58
|
+
scheme, _, param = authorization_header_value.partition(" ")
|
59
|
+
return scheme, param
|
60
|
+
|
61
|
+
def user_data_from_token(self, token: str, **kwargs) -> UserData | None:
|
62
|
+
"""Return the user associated with a token value."""
|
63
|
+
try:
|
64
|
+
# header = jwt.get_unverified_header(token)
|
65
|
+
# jwks_url = header["jwk_url"]
|
66
|
+
jwks_client = self.get_jwks_keys()
|
67
|
+
signing_key = jwks_client.get_signing_key_from_jwt(token)
|
68
|
+
decoded = jwt.decode(
|
69
|
+
token,
|
70
|
+
signing_key.key,
|
71
|
+
algorithms=["RS256"],
|
72
|
+
)
|
73
|
+
decoded["token"] = token
|
74
|
+
|
75
|
+
except jwt.exceptions.ExpiredSignatureError:
|
76
|
+
if kwargs.get("raise_exception"):
|
77
|
+
raise USSOException(status_code=401, error="expired_signature")
|
78
|
+
return None
|
79
|
+
except jwt.exceptions.InvalidSignatureError:
|
80
|
+
if kwargs.get("raise_exception"):
|
81
|
+
raise USSOException(status_code=401, error="invalid_signature")
|
82
|
+
return None
|
83
|
+
except jwt.exceptions.InvalidTokenError:
|
84
|
+
if kwargs.get("raise_exception"):
|
85
|
+
raise USSOException(
|
86
|
+
status_code=401,
|
87
|
+
error="invalid_token",
|
88
|
+
)
|
89
|
+
return None
|
90
|
+
except Exception as e:
|
91
|
+
if kwargs.get("raise_exception"):
|
92
|
+
raise USSOException(
|
93
|
+
status_code=401,
|
94
|
+
error="error",
|
95
|
+
message=str(e),
|
96
|
+
)
|
97
|
+
logger.error(e)
|
98
|
+
return None
|
99
|
+
|
100
|
+
return UserData(**decoded)
|
@@ -0,0 +1 @@
|
|
1
|
+
from .integration import jwt_access_security, jwt_access_security_ws
|
@@ -3,7 +3,7 @@ import logging
|
|
3
3
|
from fastapi import Request, WebSocket
|
4
4
|
from starlette.status import HTTP_401_UNAUTHORIZED
|
5
5
|
|
6
|
-
from usso.core import UserData,
|
6
|
+
from usso.core import UserData, Usso
|
7
7
|
from usso.exceptions import USSOException
|
8
8
|
|
9
9
|
logger = logging.getLogger("usso")
|
@@ -14,14 +14,14 @@ async def jwt_access_security(request: Request) -> UserData | None:
|
|
14
14
|
kwargs = {}
|
15
15
|
authorization = request.headers.get("Authorization")
|
16
16
|
if authorization:
|
17
|
-
scheme, _, credentials = get_authorization_scheme_param(authorization)
|
17
|
+
scheme, _, credentials = Usso().get_authorization_scheme_param(authorization)
|
18
18
|
if scheme.lower() == "bearer":
|
19
19
|
token = credentials
|
20
|
-
return user_data_from_token(token, **kwargs)
|
20
|
+
return Usso().user_data_from_token(token, **kwargs)
|
21
21
|
|
22
22
|
cookie_token = request.cookies.get("usso_access_token")
|
23
23
|
if cookie_token:
|
24
|
-
return user_data_from_token(cookie_token, **kwargs)
|
24
|
+
return Usso().user_data_from_token(cookie_token, **kwargs)
|
25
25
|
|
26
26
|
if kwargs.get("raise_exception", True):
|
27
27
|
raise USSOException(
|
@@ -36,14 +36,14 @@ async def jwt_access_security_ws(websocket: WebSocket) -> UserData | None:
|
|
36
36
|
kwargs = {}
|
37
37
|
authorization = websocket.headers.get("Authorization")
|
38
38
|
if authorization:
|
39
|
-
scheme, _, credentials = get_authorization_scheme_param(authorization)
|
39
|
+
scheme, _, credentials = Usso().get_authorization_scheme_param(authorization)
|
40
40
|
if scheme.lower() == "bearer":
|
41
41
|
token = credentials
|
42
|
-
return user_data_from_token(token, **kwargs)
|
42
|
+
return Usso().user_data_from_token(token, **kwargs)
|
43
43
|
|
44
44
|
cookie_token = websocket.cookies.get("usso_access_token")
|
45
45
|
if cookie_token:
|
46
|
-
return user_data_from_token(cookie_token, **kwargs)
|
46
|
+
return Usso().user_data_from_token(cookie_token, **kwargs)
|
47
47
|
|
48
48
|
if kwargs.get("raise_exception", True):
|
49
49
|
raise USSOException(
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: usso
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.16.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>
|
@@ -24,11 +24,11 @@ License: Copyright (c) 2016 The Python Packaging Authority (PyPA)
|
|
24
24
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
25
25
|
SOFTWARE.
|
26
26
|
|
27
|
-
Project-URL: Homepage, https://github.com/
|
28
|
-
Project-URL: Bug Reports, https://github.com/
|
29
|
-
Project-URL: Funding, https://github.com/
|
30
|
-
Project-URL: Say Thanks!, https://github.com/
|
31
|
-
Project-URL: Source, https://github.com/
|
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://github.com/ussoio/usso-python
|
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
|
34
34
|
Classifier: Intended Audience :: Developers
|
@@ -48,6 +48,7 @@ Requires-Dist: peppercorn
|
|
48
48
|
Requires-Dist: pydantic>=1.8.2
|
49
49
|
Requires-Dist: requests>=2.26.0
|
50
50
|
Requires-Dist: pyjwt[crypto]
|
51
|
+
Requires-Dist: singleton_package
|
51
52
|
Provides-Extra: fastapi
|
52
53
|
Requires-Dist: fastapi>=0.65.0; extra == "fastapi"
|
53
54
|
Requires-Dist: uvicorn[standard]>=0.13.0; extra == "fastapi"
|
usso-0.14.0/src/usso/core.py
DELETED
@@ -1,97 +0,0 @@
|
|
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)
|
@@ -1 +0,0 @@
|
|
1
|
-
from .integration import jwt_access_security
|
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
|
File without changes
|
File without changes
|