FastAPI-UI-Auth 0.2.3__py3-none-any.whl → 0.3.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.
- {fastapi_ui_auth-0.2.3.dist-info → fastapi_ui_auth-0.3.1.dist-info}/METADATA +8 -3
- fastapi_ui_auth-0.3.1.dist-info/RECORD +18 -0
- uiauth/__init__.py +3 -4
- uiauth/enums.py +0 -15
- uiauth/models.py +1 -24
- uiauth/service.py +52 -44
- uiauth/utils.py +18 -7
- uiauth/version.py +1 -1
- fastapi_ui_auth-0.2.3.dist-info/RECORD +0 -18
- {fastapi_ui_auth-0.2.3.dist-info → fastapi_ui_auth-0.3.1.dist-info}/WHEEL +0 -0
- {fastapi_ui_auth-0.2.3.dist-info → fastapi_ui_auth-0.3.1.dist-info}/licenses/LICENSE +0 -0
- {fastapi_ui_auth-0.2.3.dist-info → fastapi_ui_auth-0.3.1.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: FastAPI-UI-Auth
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Python module to add username and password authentication to specific FastAPI routes
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -12,6 +12,10 @@ Provides-Extra: dev
|
|
|
12
12
|
Requires-Dist: websockets==15.0.*; extra == "dev"
|
|
13
13
|
Requires-Dist: pre-commit==4.2.*; extra == "dev"
|
|
14
14
|
Requires-Dist: uvicorn==0.34.*; extra == "dev"
|
|
15
|
+
Provides-Extra: test
|
|
16
|
+
Requires-Dist: pytest; extra == "test"
|
|
17
|
+
Requires-Dist: pytest-cov; extra == "test"
|
|
18
|
+
Requires-Dist: httpx; extra == "test"
|
|
15
19
|
Dynamic: license-file
|
|
16
20
|
|
|
17
21
|
# FastAPIUIAuth
|
|
@@ -44,6 +48,7 @@ pip install FastAPI-UI-Auth
|
|
|
44
48
|
import uiauth
|
|
45
49
|
|
|
46
50
|
from fastapi import FastAPI
|
|
51
|
+
from fastapi.routing import APIRoute
|
|
47
52
|
|
|
48
53
|
app = FastAPI()
|
|
49
54
|
|
|
@@ -56,9 +61,9 @@ async def private_route():
|
|
|
56
61
|
|
|
57
62
|
uiauth.protect(
|
|
58
63
|
app=app,
|
|
59
|
-
|
|
64
|
+
routes=APIRoute(
|
|
60
65
|
path="/private",
|
|
61
|
-
|
|
66
|
+
endpoint=private_route
|
|
62
67
|
)
|
|
63
68
|
)
|
|
64
69
|
```
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
fastapi_ui_auth-0.3.1.dist-info/licenses/LICENSE,sha256=_sOIKJWdD2o1WwwDIwYB2qTP2nlSWqT5Tyg9jr1Xa4w,1070
|
|
2
|
+
uiauth/__init__.py,sha256=hbHN-Vv4xTxDqpQW2lmgdl-OlEkAtL6JXAGL-nucaOU,211
|
|
3
|
+
uiauth/endpoints.py,sha256=CJteXsGQWfn--U6_VdVN8VhnPsf3HSefYX1NOzAhacM,2837
|
|
4
|
+
uiauth/enums.py,sha256=W_9U2luXbscyBEROXqEhKY5DHpJRV1J4VUOpkyUOOzU,334
|
|
5
|
+
uiauth/logger.py,sha256=z67PBMs4zWOfy-Gfm_41dj5Uulm-ChvZxB_jmYKKXeI,391
|
|
6
|
+
uiauth/models.py,sha256=56d8O9bExxwPZcOzMYL0IN9LOnVTJyfOSvD58kzTklc,3210
|
|
7
|
+
uiauth/secure.py,sha256=ZOH6kT4BD56VqwaKdKocX7eSE8tqZcu-tK0QOmjY58k,1089
|
|
8
|
+
uiauth/service.py,sha256=XeVFxWR5k7QIdgxjRBiUaE0oYpSlX3HE-RadJq-7HW4,7827
|
|
9
|
+
uiauth/utils.py,sha256=Ga8RivN3PJX8zg2uu3RfEtJLGKaT1_iwphqvhh2XrPY,7007
|
|
10
|
+
uiauth/version.py,sha256=sEAhGxRzEBE5t0VjAcJ-336II62pGIQ0eLrs42I-sGU,18
|
|
11
|
+
uiauth/templates/index.html,sha256=n8tOiKXEUI4zBh1YOQNlH5MKNMRTQ2adH0QIuvrEcv4,9071
|
|
12
|
+
uiauth/templates/logout.html,sha256=JrWBJCbK1E4NfrNipMsLzfJ_-Fs2C6D4S0B6O7JNoek,3504
|
|
13
|
+
uiauth/templates/session.html,sha256=EL4gajOED3IcOnrALMiJ2SzJl2at8GFfruTuExhgOVI,3040
|
|
14
|
+
uiauth/templates/unauthorized.html,sha256=ahv78zLM04_Lu83LdX0Ua_toKeP5JZkYsTCWCrfCvHA,3002
|
|
15
|
+
fastapi_ui_auth-0.3.1.dist-info/METADATA,sha256=UbiHi5QjWiG7VqUYCbRQGpVj6_TEW3ozAYBQi4cNOpU,3662
|
|
16
|
+
fastapi_ui_auth-0.3.1.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91
|
|
17
|
+
fastapi_ui_auth-0.3.1.dist-info/top_level.txt,sha256=ra3nGTbDTgQ7eChlkngJ7xGXhSCeFTWMvb_b6q8uPVA,7
|
|
18
|
+
fastapi_ui_auth-0.3.1.dist-info/RECORD,,
|
uiauth/__init__.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
from uiauth.enums import APIEndpoints
|
|
2
|
-
from uiauth.
|
|
3
|
-
from uiauth.service import FastAPIUIAuth # noqa: F401,E402
|
|
1
|
+
from uiauth.enums import APIEndpoints # noqa: F401,E402
|
|
2
|
+
from uiauth.service import FastAPIUIAuth as _authProduct # noqa: F401,E402
|
|
4
3
|
from uiauth.version import version # noqa: F401,E402
|
|
5
4
|
|
|
6
|
-
protect =
|
|
5
|
+
protect = _authProduct
|
uiauth/enums.py
CHANGED
|
@@ -1,21 +1,6 @@
|
|
|
1
1
|
from enum import StrEnum
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
class APIMethods(StrEnum):
|
|
5
|
-
"""HTTP methods for API requests.
|
|
6
|
-
|
|
7
|
-
>>> APIMethods
|
|
8
|
-
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
GET = "GET"
|
|
12
|
-
POST = "POST"
|
|
13
|
-
PUT = "PUT"
|
|
14
|
-
DELETE = "DELETE"
|
|
15
|
-
PATCH = "PATCH"
|
|
16
|
-
OPTIONS = "OPTIONS"
|
|
17
|
-
|
|
18
|
-
|
|
19
4
|
class APIEndpoints(StrEnum):
|
|
20
5
|
"""API endpoints for all the routes.
|
|
21
6
|
|
uiauth/models.py
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import pathlib
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import Dict, Iterable, Optional
|
|
4
4
|
|
|
5
|
-
from fastapi.routing import APIRoute, APIWebSocketRoute
|
|
6
5
|
from fastapi.templating import Jinja2Templates
|
|
7
6
|
from pydantic import BaseModel, Field
|
|
8
7
|
|
|
9
|
-
from uiauth.enums import APIMethods
|
|
10
|
-
|
|
11
8
|
templates = Jinja2Templates(directory=pathlib.Path(__file__).parent / "templates")
|
|
12
9
|
|
|
13
10
|
|
|
@@ -58,26 +55,6 @@ def env_loader(**kwargs) -> EnvConfig:
|
|
|
58
55
|
env = EnvConfig
|
|
59
56
|
|
|
60
57
|
|
|
61
|
-
class Parameters(BaseModel):
|
|
62
|
-
"""Parameters for the Authenticator class.
|
|
63
|
-
|
|
64
|
-
>>> Parameters
|
|
65
|
-
|
|
66
|
-
Attributes:
|
|
67
|
-
path: Path for the secure route, must start with '/'.
|
|
68
|
-
function: Function to be called for secure routes after authentication.
|
|
69
|
-
methods: List of HTTP methods that the secure function will handle.
|
|
70
|
-
route: Type of route to be used for secure routes, either APIWebSocketRoute or APIRoute.
|
|
71
|
-
"""
|
|
72
|
-
|
|
73
|
-
path: str = Field(
|
|
74
|
-
pattern="^/.*$", description="Path for the secure route, must start with '/'"
|
|
75
|
-
)
|
|
76
|
-
function: Callable
|
|
77
|
-
methods: List[APIMethods] = [APIMethods.GET]
|
|
78
|
-
route: Type[APIWebSocketRoute] | Type[APIRoute] = APIRoute
|
|
79
|
-
|
|
80
|
-
|
|
81
58
|
class WSSession(BaseModel):
|
|
82
59
|
"""Object to store websocket session information.
|
|
83
60
|
|
uiauth/service.py
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
|
+
import inspect
|
|
1
2
|
import logging
|
|
2
|
-
|
|
3
|
+
import time
|
|
3
4
|
from typing import Dict, List
|
|
4
5
|
|
|
5
|
-
from fastapi import status
|
|
6
|
-
from fastapi.applications import FastAPI
|
|
7
|
-
from fastapi.exceptions import HTTPException
|
|
8
|
-
from fastapi.params import Depends
|
|
9
|
-
from fastapi.requests import Request
|
|
10
|
-
from fastapi.responses import Response
|
|
6
|
+
from fastapi import Depends, FastAPI, HTTPException, Request, Response, status
|
|
11
7
|
from fastapi.routing import APIRoute, APIWebSocketRoute
|
|
12
8
|
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
|
13
9
|
|
|
@@ -24,13 +20,10 @@ class FastAPIUIAuth:
|
|
|
24
20
|
|
|
25
21
|
"""
|
|
26
22
|
|
|
27
|
-
# TODO: Consume APIRoute or APIWebSocketRoute directly in params instead of creating them in the _secure method
|
|
28
|
-
# Include stricter type validation (if that works)
|
|
29
|
-
# Update samples and readme - major version change
|
|
30
23
|
def __init__(
|
|
31
24
|
self,
|
|
32
25
|
app: FastAPI,
|
|
33
|
-
|
|
26
|
+
routes: APIRoute | APIWebSocketRoute | List[APIRoute] | List[APIWebSocketRoute],
|
|
34
27
|
timeout: int = 300,
|
|
35
28
|
username: str = None,
|
|
36
29
|
password: str = None,
|
|
@@ -42,7 +35,7 @@ class FastAPIUIAuth:
|
|
|
42
35
|
|
|
43
36
|
Args:
|
|
44
37
|
app: FastAPI application instance to which the authenticator will be added.
|
|
45
|
-
|
|
38
|
+
routes: APIRoute or APIWebSocketRoute instance(s) representing the routes to be protected by authentication.
|
|
46
39
|
timeout: Session timeout in seconds, default is 300 seconds (5 minutes).
|
|
47
40
|
username: Username for authentication, can be set via environment variable 'USERNAME'.
|
|
48
41
|
password: Password for authentication, can be set via environment variable 'PASSWORD'.
|
|
@@ -50,7 +43,10 @@ class FastAPIUIAuth:
|
|
|
50
43
|
fallback_path: Fallback path to redirect to in case of session timeout or invalid session.
|
|
51
44
|
custom_logger: Custom logger instance, defaults to the custom logger.
|
|
52
45
|
"""
|
|
53
|
-
|
|
46
|
+
assert (
|
|
47
|
+
isinstance(timeout, int) and timeout > 29
|
|
48
|
+
), "Timeout must be an integer at least 30 seconds"
|
|
49
|
+
models.env = models.env_loader(username=username, password=password)
|
|
54
50
|
assert (
|
|
55
51
|
models.env.username and models.env.password
|
|
56
52
|
), "Username and password must be provided either as arguments or environment variables"
|
|
@@ -58,18 +54,18 @@ class FastAPIUIAuth:
|
|
|
58
54
|
|
|
59
55
|
self.app = app
|
|
60
56
|
|
|
61
|
-
if isinstance(
|
|
62
|
-
assert len(
|
|
63
|
-
for
|
|
64
|
-
assert isinstance(
|
|
65
|
-
|
|
66
|
-
), f"{
|
|
67
|
-
self.
|
|
68
|
-
elif isinstance(
|
|
69
|
-
self.
|
|
57
|
+
if isinstance(routes, list):
|
|
58
|
+
assert len(routes) > 0, "No endpoints to register"
|
|
59
|
+
for route in routes:
|
|
60
|
+
assert isinstance(route, APIRoute) or isinstance(
|
|
61
|
+
route, APIWebSocketRoute
|
|
62
|
+
), f"{route} must be an instance of APIRoute or APIWebSocketRoute"
|
|
63
|
+
self.routes = routes
|
|
64
|
+
elif isinstance(routes, APIRoute) or isinstance(routes, APIWebSocketRoute):
|
|
65
|
+
self.routes = [routes]
|
|
70
66
|
else:
|
|
71
67
|
raise ValueError(
|
|
72
|
-
"
|
|
68
|
+
"Routes must be an instance of APIRoute or APIWebSocketRoute or a list of them"
|
|
73
69
|
)
|
|
74
70
|
|
|
75
71
|
assert fallback_path.startswith("/"), "Fallback path must start with '/'"
|
|
@@ -90,7 +86,7 @@ class FastAPIUIAuth:
|
|
|
90
86
|
self.timeout = timeout
|
|
91
87
|
|
|
92
88
|
self._secure()
|
|
93
|
-
logger.CUSTOM_LOGGER.debug("Endpoints registered: %s", len(self.
|
|
89
|
+
logger.CUSTOM_LOGGER.debug("Endpoints registered: %s", len(self.routes))
|
|
94
90
|
|
|
95
91
|
def _verify_auth(
|
|
96
92
|
self,
|
|
@@ -125,12 +121,11 @@ class FastAPIUIAuth:
|
|
|
125
121
|
samesite="strict",
|
|
126
122
|
max_age=self.timeout,
|
|
127
123
|
)
|
|
124
|
+
models.ws_session.client_auth[request.client.host] = {
|
|
125
|
+
"token": session_token,
|
|
126
|
+
"expires_at": time.time() + self.timeout,
|
|
127
|
+
}
|
|
128
128
|
response.delete_cookie(key="X-Requested-By")
|
|
129
|
-
Timer(
|
|
130
|
-
function=utils.clear_session,
|
|
131
|
-
args=(request.client.host,),
|
|
132
|
-
interval=self.timeout,
|
|
133
|
-
).start()
|
|
134
129
|
return {"redirect_url": destination}
|
|
135
130
|
raise HTTPException(
|
|
136
131
|
status_code=status.HTTP_417_EXPECTATION_FAILED,
|
|
@@ -145,42 +140,55 @@ class FastAPIUIAuth:
|
|
|
145
140
|
path=enums.APIEndpoints.fastapi_login,
|
|
146
141
|
endpoint=endpoints.login,
|
|
147
142
|
methods=["GET"],
|
|
143
|
+
include_in_schema=False,
|
|
148
144
|
)
|
|
149
145
|
logout_route = APIRoute(
|
|
150
146
|
path=enums.APIEndpoints.fastapi_logout,
|
|
151
147
|
endpoint=endpoints.logout,
|
|
152
148
|
methods=["GET"],
|
|
149
|
+
include_in_schema=False,
|
|
153
150
|
)
|
|
154
151
|
error_route = APIRoute(
|
|
155
152
|
path=enums.APIEndpoints.fastapi_error,
|
|
156
153
|
endpoint=endpoints.error,
|
|
157
154
|
methods=["GET"],
|
|
155
|
+
include_in_schema=False,
|
|
158
156
|
)
|
|
159
157
|
session_route = APIRoute(
|
|
160
158
|
path=enums.APIEndpoints.fastapi_session,
|
|
161
159
|
endpoint=endpoints.session,
|
|
162
160
|
methods=["GET"],
|
|
161
|
+
include_in_schema=False,
|
|
163
162
|
)
|
|
164
163
|
verify_route = APIRoute(
|
|
165
164
|
path=enums.APIEndpoints.fastapi_verify_login,
|
|
166
165
|
endpoint=self._verify_auth,
|
|
167
166
|
methods=["POST"],
|
|
167
|
+
include_in_schema=False,
|
|
168
168
|
)
|
|
169
|
-
for
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
169
|
+
protected_paths = {route.path for route in self.routes}
|
|
170
|
+
conflicting = [
|
|
171
|
+
route
|
|
172
|
+
for route in self.app.routes
|
|
173
|
+
if isinstance(route, (APIRoute, APIWebSocketRoute))
|
|
174
|
+
and route.path in protected_paths
|
|
175
|
+
]
|
|
176
|
+
for existing in conflicting:
|
|
177
|
+
logger.CUSTOM_LOGGER.warning(
|
|
178
|
+
"Route %s already registered in the app, removing and re-registering with authentication",
|
|
179
|
+
existing.path,
|
|
180
|
+
)
|
|
181
|
+
self.app.routes.remove(existing)
|
|
182
|
+
for route in self.routes:
|
|
183
|
+
kwargs = {
|
|
184
|
+
name: getattr(route, name)
|
|
185
|
+
for name in inspect.signature(route.__class__.__init__).parameters
|
|
186
|
+
if name != "self" and hasattr(route, name)
|
|
187
|
+
}
|
|
188
|
+
kwargs["dependencies"] = list(route.dependencies) + [
|
|
189
|
+
Depends(utils.verify_session)
|
|
190
|
+
]
|
|
191
|
+
secure_route = route.__class__(**kwargs)
|
|
184
192
|
self.app.routes.append(secure_route)
|
|
185
193
|
self.app.routes.extend(
|
|
186
194
|
[login_route, logout_route, session_route, verify_route, error_route]
|
uiauth/utils.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import secrets
|
|
2
|
+
import time
|
|
2
3
|
from typing import List, NoReturn
|
|
3
4
|
|
|
4
5
|
from fastapi import status
|
|
@@ -111,7 +112,6 @@ def verify_login(
|
|
|
111
112
|
if secrets.compare_digest(signature, expected_signature):
|
|
112
113
|
models.ws_session.invalid[request.client.host] = 0
|
|
113
114
|
key = secrets.token_urlsafe(64)
|
|
114
|
-
models.ws_session.client_auth[request.client.host] = key
|
|
115
115
|
return key
|
|
116
116
|
raise_error(request)
|
|
117
117
|
|
|
@@ -138,14 +138,25 @@ def verify_session(
|
|
|
138
138
|
detail="Request or WebSocket connection is required for session check.",
|
|
139
139
|
)
|
|
140
140
|
session_token = request.cookies.get("session_token")
|
|
141
|
-
|
|
141
|
+
stored = models.ws_session.client_auth.get(request.client.host, {})
|
|
142
142
|
if (
|
|
143
|
-
|
|
143
|
+
stored.get("token")
|
|
144
144
|
and session_token
|
|
145
|
-
and secrets.compare_digest(session_token,
|
|
145
|
+
and secrets.compare_digest(session_token, stored["token"])
|
|
146
146
|
):
|
|
147
|
-
|
|
148
|
-
|
|
147
|
+
if time.time() < stored["expires_at"]:
|
|
148
|
+
logger.CUSTOM_LOGGER.debug(
|
|
149
|
+
"Session is valid for host: %s", request.client.host
|
|
150
|
+
)
|
|
151
|
+
return
|
|
152
|
+
models.ws_session.client_auth.pop(request.client.host, None)
|
|
153
|
+
logger.CUSTOM_LOGGER.warning(
|
|
154
|
+
"Session expired for host: %s", request.client.host
|
|
155
|
+
)
|
|
156
|
+
raise models.RedirectException(
|
|
157
|
+
source=request.url.path,
|
|
158
|
+
destination=enums.APIEndpoints.fastapi_login,
|
|
159
|
+
)
|
|
149
160
|
elif not session_token:
|
|
150
161
|
logger.CUSTOM_LOGGER.warning(
|
|
151
162
|
"Session is invalid or expired for host: %s", request.client.host
|
|
@@ -158,7 +169,7 @@ def verify_session(
|
|
|
158
169
|
logger.CUSTOM_LOGGER.warning(
|
|
159
170
|
"Session token mismatch for host: %s. Expected: %s, Received: %s",
|
|
160
171
|
request.client.host,
|
|
161
|
-
|
|
172
|
+
stored.get("token", "None"),
|
|
162
173
|
session_token,
|
|
163
174
|
)
|
|
164
175
|
raise models.RedirectException(
|
uiauth/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
version = "0.
|
|
1
|
+
version = "0.3.1"
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
fastapi_ui_auth-0.2.3.dist-info/licenses/LICENSE,sha256=_sOIKJWdD2o1WwwDIwYB2qTP2nlSWqT5Tyg9jr1Xa4w,1070
|
|
2
|
-
uiauth/__init__.py,sha256=s8r2Z0O9w3cuw7GcmRTOWY0NZC0KJXzBS9QsG9wUWsk,264
|
|
3
|
-
uiauth/endpoints.py,sha256=CJteXsGQWfn--U6_VdVN8VhnPsf3HSefYX1NOzAhacM,2837
|
|
4
|
-
uiauth/enums.py,sha256=WO0eBv3l9HHr1I_ZXtAifCgdL-db_tZj9ka7jnjiS5k,547
|
|
5
|
-
uiauth/logger.py,sha256=z67PBMs4zWOfy-Gfm_41dj5Uulm-ChvZxB_jmYKKXeI,391
|
|
6
|
-
uiauth/models.py,sha256=cU1VoPtHsB0A8DIvXSDpAZz2KtbOtQPB507cQ4MGeOw,4014
|
|
7
|
-
uiauth/secure.py,sha256=ZOH6kT4BD56VqwaKdKocX7eSE8tqZcu-tK0QOmjY58k,1089
|
|
8
|
-
uiauth/service.py,sha256=sJG-RbiiO-qKtvaGrSsu_UfTndRPkdRn0AXWhGnjJmQ,7443
|
|
9
|
-
uiauth/utils.py,sha256=DzXqxLpKHUDy1bxffg1cw0izqxcgmnCybSytywiPgbQ,6625
|
|
10
|
-
uiauth/version.py,sha256=7YVXTLSKw_SIjam_Lv65ld1ty1jiyVmclya8_CjMMqY,18
|
|
11
|
-
uiauth/templates/index.html,sha256=n8tOiKXEUI4zBh1YOQNlH5MKNMRTQ2adH0QIuvrEcv4,9071
|
|
12
|
-
uiauth/templates/logout.html,sha256=JrWBJCbK1E4NfrNipMsLzfJ_-Fs2C6D4S0B6O7JNoek,3504
|
|
13
|
-
uiauth/templates/session.html,sha256=EL4gajOED3IcOnrALMiJ2SzJl2at8GFfruTuExhgOVI,3040
|
|
14
|
-
uiauth/templates/unauthorized.html,sha256=ahv78zLM04_Lu83LdX0Ua_toKeP5JZkYsTCWCrfCvHA,3002
|
|
15
|
-
fastapi_ui_auth-0.2.3.dist-info/METADATA,sha256=QeOE9sG3SJE5Tzqm7AoOe65z8QjgqcS_t_mSmFJHYBU,3493
|
|
16
|
-
fastapi_ui_auth-0.2.3.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91
|
|
17
|
-
fastapi_ui_auth-0.2.3.dist-info/top_level.txt,sha256=ra3nGTbDTgQ7eChlkngJ7xGXhSCeFTWMvb_b6q8uPVA,7
|
|
18
|
-
fastapi_ui_auth-0.2.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|