FastAPI-UI-Auth 0.1.1__py3-none-any.whl → 0.2.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.1.1.dist-info → fastapi_ui_auth-0.2.1.dist-info}/METADATA +38 -18
- fastapi_ui_auth-0.2.1.dist-info/RECORD +18 -0
- fastapi_ui_auth-0.2.1.dist-info/top_level.txt +1 -0
- uiauth/__init__.py +6 -0
- {fastapiauthenticator → uiauth}/endpoints.py +27 -2
- uiauth/logger.py +13 -0
- {fastapiauthenticator → uiauth}/models.py +39 -1
- {fastapiauthenticator → uiauth}/service.py +26 -15
- {fastapiauthenticator → uiauth}/templates/index.html +3 -3
- uiauth/templates/logout.html +94 -0
- {fastapiauthenticator → uiauth}/templates/session.html +3 -3
- {fastapiauthenticator → uiauth}/templates/unauthorized.html +3 -3
- {fastapiauthenticator → uiauth}/utils.py +11 -18
- uiauth/version.py +1 -0
- fastapi_ui_auth-0.1.1.dist-info/RECORD +0 -16
- fastapi_ui_auth-0.1.1.dist-info/top_level.txt +0 -1
- fastapiauthenticator/__init__.py +0 -6
- fastapiauthenticator/version.py +0 -1
- {fastapi_ui_auth-0.1.1.dist-info → fastapi_ui_auth-0.2.1.dist-info}/WHEEL +0 -0
- {fastapi_ui_auth-0.1.1.dist-info → fastapi_ui_auth-0.2.1.dist-info}/licenses/LICENSE +0 -0
- {fastapiauthenticator → uiauth}/enums.py +0 -0
- {fastapiauthenticator → uiauth}/secure.py +0 -0
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: FastAPI-UI-Auth
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.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
|
|
7
7
|
License-File: LICENSE
|
|
8
|
-
Requires-Dist: fastapi==0.
|
|
9
|
-
Requires-Dist:
|
|
10
|
-
Requires-Dist: pydantic==2.
|
|
11
|
-
Requires-Dist: python-dotenv==1.
|
|
8
|
+
Requires-Dist: fastapi==0.122.*
|
|
9
|
+
Requires-Dist: Jinja2==3.1.*
|
|
10
|
+
Requires-Dist: pydantic==2.12.*
|
|
11
|
+
Requires-Dist: python-dotenv==1.2.*
|
|
12
12
|
Provides-Extra: dev
|
|
13
13
|
Requires-Dist: websockets==15.0.*; extra == "dev"
|
|
14
14
|
Requires-Dist: pre-commit==4.2.*; extra == "dev"
|
|
15
15
|
Requires-Dist: uvicorn==0.34.*; extra == "dev"
|
|
16
16
|
Dynamic: license-file
|
|
17
17
|
|
|
18
|
-
#
|
|
18
|
+
# FastAPIUIAuth
|
|
19
19
|
|
|
20
20
|
Python module to add username and password authentication to specific FastAPI routes
|
|
21
21
|
|
|
@@ -25,20 +25,24 @@ Python module to add username and password authentication to specific FastAPI ro
|
|
|
25
25
|
|
|
26
26
|
![Platform][label-platform]
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
**Deployments**
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
repo="thevickypedia/FastAPIAuthenticator"
|
|
30
|
+
[![pypi][label-actions-pypi]][gha_pypi]
|
|
32
31
|
|
|
33
|
-
|
|
32
|
+
[![Pypi][label-pypi]][pypi]
|
|
33
|
+
[![Pypi-format][label-pypi-format]][pypi-files]
|
|
34
|
+
[![Pypi-status][label-pypi-status]][pypi]
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
```shell
|
|
39
|
+
pip install FastAPI-UI-Auth
|
|
36
40
|
```
|
|
37
41
|
|
|
38
42
|
## Usage
|
|
39
43
|
|
|
40
44
|
```python
|
|
41
|
-
import
|
|
45
|
+
import uiauth
|
|
42
46
|
|
|
43
47
|
from fastapi import FastAPI
|
|
44
48
|
|
|
@@ -51,16 +55,16 @@ async def public_route():
|
|
|
51
55
|
async def private_route():
|
|
52
56
|
return {"message": "This is a private route"}
|
|
53
57
|
|
|
54
|
-
|
|
58
|
+
uiauth.protect(
|
|
55
59
|
app=app,
|
|
56
|
-
params=
|
|
60
|
+
params=uiauth.Parameters(
|
|
57
61
|
path="/private",
|
|
58
62
|
function=private_route
|
|
59
63
|
)
|
|
60
64
|
)
|
|
61
65
|
```
|
|
62
66
|
|
|
63
|
-
> `
|
|
67
|
+
> `FastAPI-UI-Auth` supports both `APIRoute` and `APIWebSocketRoute` routes.<br>
|
|
64
68
|
> Refer [samples] directory for different use-cases.
|
|
65
69
|
|
|
66
70
|
## Coding Standards
|
|
@@ -90,6 +94,11 @@ python -m pip install sphinx==5.1.1 pre-commit recommonmark
|
|
|
90
94
|
pre-commit run --all-files
|
|
91
95
|
```
|
|
92
96
|
|
|
97
|
+
## Pypi Package
|
|
98
|
+
[![pypi-module][label-pypi-package]][pypi-repo]
|
|
99
|
+
|
|
100
|
+
[https://pypi.org/project/FastAPI-UI-Auth/][pypi]
|
|
101
|
+
|
|
93
102
|
## License & copyright
|
|
94
103
|
|
|
95
104
|
© Vignesh Rao
|
|
@@ -99,12 +108,23 @@ Licensed under the [MIT License][license]
|
|
|
99
108
|
[//]: # (Labels)
|
|
100
109
|
|
|
101
110
|
[3.11]: https://docs.python.org/3/whatsnew/3.11.html
|
|
102
|
-
[license]: https://github.com/thevickypedia/
|
|
111
|
+
[license]: https://github.com/thevickypedia/FastAPI-UI-Auth/blob/main/LICENSE
|
|
103
112
|
[google-docs]: https://google.github.io/styleguide/pyguide.html#38-comments-and-docstrings
|
|
104
113
|
[pep8]: https://www.python.org/dev/peps/pep-0008/
|
|
105
114
|
[isort]: https://pycqa.github.io/isort/
|
|
106
|
-
[samples]: https://github.com/thevickypedia/
|
|
115
|
+
[samples]: https://github.com/thevickypedia/FastAPI-UI-Auth/tree/main/samples
|
|
107
116
|
|
|
117
|
+
[label-actions-pypi]: https://github.com/thevickypedia/FastAPI-UI-Auth/actions/workflows/python-publish.yml/badge.svg
|
|
118
|
+
[label-pypi]: https://img.shields.io/pypi/v/FastAPI-UI-Auth
|
|
119
|
+
[label-pypi-format]: https://img.shields.io/pypi/format/FastAPI-UI-Auth
|
|
120
|
+
[label-pypi-status]: https://img.shields.io/pypi/status/FastAPI_UI_Auth
|
|
121
|
+
[label-pypi-package]: https://img.shields.io/badge/Pypi%20Package-FastAPI_UI_Auth-blue?style=for-the-badge&logo=Python
|
|
108
122
|
[label-pyversion]: https://img.shields.io/badge/python-3.11%20%7C%203.12-blue
|
|
109
123
|
[label-platform]: https://img.shields.io/badge/Platform-Linux|macOS|Windows-1f425f.svg
|
|
110
|
-
[release-notes]: https://github.com/thevickypedia/
|
|
124
|
+
[release-notes]: https://github.com/thevickypedia/FastAPI-UI-Auth/blob/main/release_notes.rst
|
|
125
|
+
|
|
126
|
+
[gha_pypi]: https://github.com/thevickypedia/FastAPI-UI-Auth/actions/workflows/python-publish.yml
|
|
127
|
+
|
|
128
|
+
[pypi]: https://pypi.org/project/FastAPI-UI-Auth
|
|
129
|
+
[pypi-files]: https://pypi.org/project/FastAPI-UI-Auth/#files
|
|
130
|
+
[pypi-repo]: https://packaging.python.org/tutorials/packaging-projects/
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
fastapi_ui_auth-0.2.1.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=dH207dqVybK_qPzzm2RuMxZBamoFm8zO5tk7jmyGStM,3719
|
|
7
|
+
uiauth/secure.py,sha256=ZOH6kT4BD56VqwaKdKocX7eSE8tqZcu-tK0QOmjY58k,1089
|
|
8
|
+
uiauth/service.py,sha256=DJnK9PsBg9CnWiIqWRt7Aa7ZXncTUm5W8jZmA9UJK5Q,6659
|
|
9
|
+
uiauth/utils.py,sha256=DzXqxLpKHUDy1bxffg1cw0izqxcgmnCybSytywiPgbQ,6625
|
|
10
|
+
uiauth/version.py,sha256=Ui79YHRxRfg2Yk8f7S1RGQcYuTAo3-YJSMGCUb8i3gI,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.1.dist-info/METADATA,sha256=AmSMwwfJ_6PeV1Eiz1sy0Uke9T9QUU9BZm1uNUPVfCk,3553
|
|
16
|
+
fastapi_ui_auth-0.2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
17
|
+
fastapi_ui_auth-0.2.1.dist-info/top_level.txt,sha256=ra3nGTbDTgQ7eChlkngJ7xGXhSCeFTWMvb_b6q8uPVA,7
|
|
18
|
+
fastapi_ui_auth-0.2.1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
uiauth
|
uiauth/__init__.py
ADDED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
from fastapi.exceptions import HTTPException
|
|
1
2
|
from fastapi.requests import Request
|
|
2
3
|
from fastapi.responses import HTMLResponse
|
|
3
4
|
|
|
4
|
-
from
|
|
5
|
-
from
|
|
5
|
+
from uiauth import enums, logger, models, utils
|
|
6
|
+
from uiauth.version import version
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
def session(request: Request) -> HTMLResponse:
|
|
@@ -49,6 +50,30 @@ def login(request: Request) -> HTMLResponse:
|
|
|
49
50
|
)
|
|
50
51
|
|
|
51
52
|
|
|
53
|
+
def logout(request: Request) -> HTMLResponse:
|
|
54
|
+
"""Render the logout page with the verification path and version.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
HTMLResponse:
|
|
58
|
+
Rendered HTML response for the logout page.
|
|
59
|
+
"""
|
|
60
|
+
try:
|
|
61
|
+
utils.verify_session(request)
|
|
62
|
+
except (models.RedirectException, HTTPException):
|
|
63
|
+
logger.CUSTOM_LOGGER.warning("Invalid session")
|
|
64
|
+
return session(request)
|
|
65
|
+
return utils.deauthorize(
|
|
66
|
+
models.templates.TemplateResponse(
|
|
67
|
+
name="logout.html",
|
|
68
|
+
context={
|
|
69
|
+
"request": request,
|
|
70
|
+
"detail": "You have been successfully logged out.",
|
|
71
|
+
"version": f"v{version}",
|
|
72
|
+
},
|
|
73
|
+
),
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
52
77
|
def error(request: Request) -> HTMLResponse:
|
|
53
78
|
"""Error endpoint for the authenticator.
|
|
54
79
|
|
uiauth/logger.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Default logger for FastAPI-UI-Auth package."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
CUSTOM_LOGGER = logging.getLogger(__name__)
|
|
7
|
+
CUSTOM_LOGGER.setLevel(logging.DEBUG)
|
|
8
|
+
CONSOLE_HANDLER = logging.StreamHandler(sys.stdout)
|
|
9
|
+
CONSOLE_FORMATTER = logging.Formatter(
|
|
10
|
+
fmt="%(levelname)-9s %(message)s",
|
|
11
|
+
)
|
|
12
|
+
CONSOLE_HANDLER.setFormatter(fmt=CONSOLE_FORMATTER)
|
|
13
|
+
CUSTOM_LOGGER.addHandler(hdlr=CONSOLE_HANDLER)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import os
|
|
1
2
|
import pathlib
|
|
2
3
|
from typing import Callable, Dict, List, Optional, Type
|
|
3
4
|
|
|
@@ -5,11 +6,48 @@ from fastapi.routing import APIRoute, APIWebSocketRoute
|
|
|
5
6
|
from fastapi.templating import Jinja2Templates
|
|
6
7
|
from pydantic import BaseModel, Field
|
|
7
8
|
|
|
8
|
-
from
|
|
9
|
+
from uiauth.enums import APIMethods
|
|
9
10
|
|
|
10
11
|
templates = Jinja2Templates(directory=pathlib.Path(__file__).parent / "templates")
|
|
11
12
|
|
|
12
13
|
|
|
14
|
+
def get_env(keys: List[str], default: Optional[str] = None) -> Optional[str]:
|
|
15
|
+
"""Get environment variable value.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
keys: List of environment variable names to check.
|
|
19
|
+
default: Default value if the environment variable is not set.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
Value of the environment variable or default value.
|
|
23
|
+
"""
|
|
24
|
+
for key in keys:
|
|
25
|
+
if value := os.getenv(key):
|
|
26
|
+
return value
|
|
27
|
+
if value := os.getenv(key.upper()):
|
|
28
|
+
return value
|
|
29
|
+
if value := os.getenv(key.lower()):
|
|
30
|
+
return value
|
|
31
|
+
return default
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class EnvConfig(BaseModel):
|
|
35
|
+
"""Configuration for environment variables.
|
|
36
|
+
|
|
37
|
+
>>> EnvConfig
|
|
38
|
+
|
|
39
|
+
See Also:
|
|
40
|
+
- Tries to resolve username and password through kwargs.
|
|
41
|
+
- Uses environment variables as fallback.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
username: str = Field(default_factory=lambda: get_env(("username", "user")))
|
|
45
|
+
password: str = Field(default_factory=lambda: get_env(("password", "pass")))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
env = EnvConfig
|
|
49
|
+
|
|
50
|
+
|
|
13
51
|
class Parameters(BaseModel):
|
|
14
52
|
"""Parameters for the Authenticator class.
|
|
15
53
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
import os
|
|
3
2
|
from threading import Timer
|
|
4
3
|
from typing import Dict, List
|
|
5
4
|
|
|
@@ -13,18 +12,17 @@ from fastapi.responses import Response
|
|
|
13
12
|
from fastapi.routing import APIRoute, APIWebSocketRoute
|
|
14
13
|
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
|
15
14
|
|
|
16
|
-
from
|
|
15
|
+
from uiauth import endpoints, enums, logger, models, utils
|
|
17
16
|
|
|
18
17
|
dotenv.load_dotenv(dotenv_path=dotenv.find_dotenv(), override=True)
|
|
19
|
-
LOGGER = logging.getLogger("uvicorn.default")
|
|
20
18
|
BEARER_AUTH = HTTPBearer()
|
|
21
19
|
|
|
22
20
|
|
|
23
21
|
# noinspection PyDefaultArgument
|
|
24
|
-
class
|
|
25
|
-
"""
|
|
22
|
+
class FastAPIUIAuth:
|
|
23
|
+
"""FastAPIUIAuth is a FastAPI integration that provides authentication for secure routes.
|
|
26
24
|
|
|
27
|
-
>>>
|
|
25
|
+
>>> FastAPIUIAuth
|
|
28
26
|
|
|
29
27
|
"""
|
|
30
28
|
|
|
@@ -33,10 +31,11 @@ class Authenticator:
|
|
|
33
31
|
app: FastAPI,
|
|
34
32
|
params: models.Parameters | List[models.Parameters],
|
|
35
33
|
timeout: int = 300,
|
|
36
|
-
username: str =
|
|
37
|
-
password: str =
|
|
34
|
+
username: str = None,
|
|
35
|
+
password: str = None,
|
|
38
36
|
fallback_button: str = models.fallback.button,
|
|
39
37
|
fallback_path: str = models.fallback.path,
|
|
38
|
+
custom_logger: logging.Logger = None,
|
|
40
39
|
):
|
|
41
40
|
"""Initialize the APIAuthenticator with the FastAPI app and secure function.
|
|
42
41
|
|
|
@@ -48,8 +47,9 @@ class Authenticator:
|
|
|
48
47
|
password: Password for authentication, can be set via environment variable 'PASSWORD'.
|
|
49
48
|
fallback_button: Title for the fallback button, defaults to "LOGIN".
|
|
50
49
|
fallback_path: Fallback path to redirect to in case of session timeout or invalid session.
|
|
50
|
+
custom_logger: Custom logger instance, defaults to the custom logger.
|
|
51
51
|
"""
|
|
52
|
-
|
|
52
|
+
models.env = models.EnvConfig(username=username, password=password)
|
|
53
53
|
assert fallback_path.startswith("/"), "Fallback path must start with '/'"
|
|
54
54
|
|
|
55
55
|
self.app = app
|
|
@@ -68,11 +68,15 @@ class Authenticator:
|
|
|
68
68
|
handler=utils.redirect_exception_handler,
|
|
69
69
|
)
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
if custom_logger:
|
|
72
|
+
assert isinstance(
|
|
73
|
+
custom_logger, logging.Logger
|
|
74
|
+
), "Custom logger must be an instance of logging.Logger"
|
|
75
|
+
logger.CUSTOM_LOGGER = custom_logger
|
|
73
76
|
self.timeout = timeout
|
|
74
77
|
|
|
75
78
|
self._secure()
|
|
79
|
+
logger.CUSTOM_LOGGER.debug("Endpoints registered: %s", len(self.params))
|
|
76
80
|
|
|
77
81
|
def _verify_auth(
|
|
78
82
|
self,
|
|
@@ -94,11 +98,11 @@ class Authenticator:
|
|
|
94
98
|
session_token = utils.verify_login(
|
|
95
99
|
authorization=authorization,
|
|
96
100
|
request=request,
|
|
97
|
-
env_username=self.username,
|
|
98
|
-
env_password=self.password,
|
|
99
101
|
)
|
|
100
102
|
if destination := request.cookies.get("X-Requested-By"):
|
|
101
|
-
|
|
103
|
+
logger.CUSTOM_LOGGER.info(
|
|
104
|
+
"Setting session timeout for %s seconds", self.timeout
|
|
105
|
+
)
|
|
102
106
|
# Set session_token cookie with a timeout, to be used for session validation when redirected
|
|
103
107
|
response.set_cookie(
|
|
104
108
|
key="session_token",
|
|
@@ -128,6 +132,11 @@ class Authenticator:
|
|
|
128
132
|
endpoint=endpoints.login,
|
|
129
133
|
methods=["GET"],
|
|
130
134
|
)
|
|
135
|
+
logout_route = APIRoute(
|
|
136
|
+
path=enums.APIEndpoints.fastapi_logout,
|
|
137
|
+
endpoint=endpoints.logout,
|
|
138
|
+
methods=["GET"],
|
|
139
|
+
)
|
|
131
140
|
error_route = APIRoute(
|
|
132
141
|
path=enums.APIEndpoints.fastapi_error,
|
|
133
142
|
endpoint=endpoints.error,
|
|
@@ -159,4 +168,6 @@ class Authenticator:
|
|
|
159
168
|
dependencies=[Depends(utils.verify_session)],
|
|
160
169
|
)
|
|
161
170
|
self.app.routes.append(secure_route)
|
|
162
|
-
self.app.routes.extend(
|
|
171
|
+
self.app.routes.extend(
|
|
172
|
+
[login_route, logout_route, session_route, verify_route, error_route]
|
|
173
|
+
)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<html lang="en">
|
|
4
4
|
<head>
|
|
5
5
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
6
|
-
<title>FastAPI
|
|
6
|
+
<title>FastAPI UI Authentication</title>
|
|
7
7
|
<meta property="og:type" content="Authenticator">
|
|
8
8
|
<meta name="keywords" content="Python, fastapi, JavaScript, HTML, CSS">
|
|
9
9
|
<meta name="author" content="Vignesh Rao">
|
|
@@ -239,8 +239,8 @@
|
|
|
239
239
|
</script>
|
|
240
240
|
<footer>
|
|
241
241
|
<div class="footer">
|
|
242
|
-
|
|
243
|
-
<a href="https://github.com/thevickypedia/
|
|
242
|
+
FastAPI-UI-Auth - {{ version }}<br>
|
|
243
|
+
<a href="https://github.com/thevickypedia/FastAPI-UI-Auth">https://github.com/thevickypedia/FastAPI-UI-Auth</a>
|
|
244
244
|
</div>
|
|
245
245
|
</footer>
|
|
246
246
|
<script>
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
5
|
+
<title>FastAPI UI Authentication</title>
|
|
6
|
+
<meta property="og:type" content="Authenticator">
|
|
7
|
+
<meta name="keywords" content="Python, fastapi, JavaScript, HTML, CSS">
|
|
8
|
+
<meta name="author" content="Vignesh Rao">
|
|
9
|
+
<meta content="width=device-width, initial-scale=1" name="viewport">
|
|
10
|
+
<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
|
|
11
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.js"></script>
|
|
12
|
+
<!-- Favicon.ico and Apple Touch Icon -->
|
|
13
|
+
<link property="og:image" rel="icon" href="https://thevickypedia.github.io/open-source/images/logo/fastapi.ico">
|
|
14
|
+
<link property="og:image" rel="apple-touch-icon"
|
|
15
|
+
href="https://thevickypedia.github.io/open-source/images/logo/fastapi.png">
|
|
16
|
+
<!-- Font Awesome icons -->
|
|
17
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/fontawesome.min.css">
|
|
18
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/solid.min.css">
|
|
19
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/regular.min.css">
|
|
20
|
+
<style>
|
|
21
|
+
img {
|
|
22
|
+
display: block;
|
|
23
|
+
margin-left: auto;
|
|
24
|
+
margin-right: auto;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
:is(h1, h2, h3, h4, h5, h6) {
|
|
28
|
+
text-align: center;
|
|
29
|
+
color: #F0F0F0;
|
|
30
|
+
font-family: 'Courier New', sans-serif;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
button {
|
|
34
|
+
width: 100px;
|
|
35
|
+
margin: 0 auto;
|
|
36
|
+
display: block;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
body {
|
|
40
|
+
background-color: #151515;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
footer {
|
|
44
|
+
bottom: 20px;
|
|
45
|
+
position: fixed;
|
|
46
|
+
width: 100%;
|
|
47
|
+
color: #fff;
|
|
48
|
+
text-align: center;
|
|
49
|
+
font-family: 'Courier New', monospace;
|
|
50
|
+
font-size: small;
|
|
51
|
+
}
|
|
52
|
+
</style>
|
|
53
|
+
<noscript>
|
|
54
|
+
<style>
|
|
55
|
+
body {
|
|
56
|
+
width: 100%;
|
|
57
|
+
height: 100%;
|
|
58
|
+
overflow: hidden;
|
|
59
|
+
}
|
|
60
|
+
</style>
|
|
61
|
+
<div style="position: fixed; text-align:center; height: 100%; width: 100%; background-color: #151515;">
|
|
62
|
+
<h2 style="margin-top:5%">This page requires JavaScript
|
|
63
|
+
to be enabled.
|
|
64
|
+
<br><br>
|
|
65
|
+
Please refer <a href="https://www.enable-javascript.com/">enable-javascript</a> for how to.
|
|
66
|
+
</h2>
|
|
67
|
+
<form>
|
|
68
|
+
<button type="submit" onClick="<meta httpEquiv='refresh' content='0'>">RETRY</button>
|
|
69
|
+
</form>
|
|
70
|
+
</div>
|
|
71
|
+
</noscript>
|
|
72
|
+
</head>
|
|
73
|
+
<body>
|
|
74
|
+
<h2 style="margin-top:5%">LOGOUT</h2>
|
|
75
|
+
<h3>{{ detail }}</h3>
|
|
76
|
+
<p>
|
|
77
|
+
<img src="https://thevickypedia.github.io/open-source/images/gif/blended_fusion.gif"
|
|
78
|
+
onerror="this.src='https://vigneshrao.com/open-source/images/gif/blended_fusion.gif'"
|
|
79
|
+
width="200" height="200" alt="Image" class="center">
|
|
80
|
+
</p>
|
|
81
|
+
{% if show_login %}
|
|
82
|
+
<button style="text-align:center" onClick="window.location.href = '/monitor';">LOGIN</button>
|
|
83
|
+
{% else %}
|
|
84
|
+
<h3>Please close the session window</h3>
|
|
85
|
+
{% endif %}
|
|
86
|
+
<h4>Click <a href="https://vigneshrao.com/contact">HERE</a> to reach out.</h4>
|
|
87
|
+
</body>
|
|
88
|
+
<footer>
|
|
89
|
+
<div class="footer">
|
|
90
|
+
FastAPI-UI-Auth - {{ version }}<br>
|
|
91
|
+
<a href="https://github.com/thevickypedia/FastAPI-UI-Auth">https://github.com/thevickypedia/FastAPI-UI-Auth</a>
|
|
92
|
+
</div>
|
|
93
|
+
</footer>
|
|
94
|
+
</html>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
|
5
|
-
<title>FastAPI
|
|
5
|
+
<title>FastAPI UI Authentication</title>
|
|
6
6
|
<meta property="og:type" content="Authenticator">
|
|
7
7
|
<meta name="keywords" content="Python, fastapi, JavaScript, HTML, CSS">
|
|
8
8
|
<meta name="author" content="Vignesh Rao">
|
|
@@ -79,8 +79,8 @@
|
|
|
79
79
|
</body>
|
|
80
80
|
<footer>
|
|
81
81
|
<div class="footer">
|
|
82
|
-
|
|
83
|
-
<a href="https://github.com/thevickypedia/
|
|
82
|
+
FastAPI-UI-Auth - {{ version }}<br>
|
|
83
|
+
<a href="https://github.com/thevickypedia/FastAPI-UI-Auth">https://github.com/thevickypedia/FastAPI-UI-Auth</a>
|
|
84
84
|
</div>
|
|
85
85
|
</footer>
|
|
86
86
|
</html>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
|
5
|
-
<title>FastAPI
|
|
5
|
+
<title>FastAPI UI Authentication</title>
|
|
6
6
|
<meta property="og:type" content="Authenticator">
|
|
7
7
|
<meta name="keywords" content="Python, fastapi, JavaScript, HTML, CSS">
|
|
8
8
|
<meta name="author" content="Vignesh Rao">
|
|
@@ -79,8 +79,8 @@
|
|
|
79
79
|
</body>
|
|
80
80
|
<footer>
|
|
81
81
|
<div class="footer">
|
|
82
|
-
|
|
83
|
-
<a href="https://github.com/thevickypedia/
|
|
82
|
+
FastAPI-UI-Auth - {{ version }}<br>
|
|
83
|
+
<a href="https://github.com/thevickypedia/FastAPI-UI-Auth">https://github.com/thevickypedia/FastAPI-UI-Auth</a>
|
|
84
84
|
</div>
|
|
85
85
|
</footer>
|
|
86
86
|
</html>
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import logging
|
|
2
1
|
import secrets
|
|
3
2
|
from typing import List, NoReturn
|
|
4
3
|
|
|
@@ -9,9 +8,7 @@ from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse
|
|
|
9
8
|
from fastapi.security import HTTPAuthorizationCredentials
|
|
10
9
|
from fastapi.websockets import WebSocket
|
|
11
10
|
|
|
12
|
-
from
|
|
13
|
-
|
|
14
|
-
LOGGER = logging.getLogger("uvicorn.default")
|
|
11
|
+
from uiauth import enums, logger, models, secure
|
|
15
12
|
|
|
16
13
|
|
|
17
14
|
def failed_auth_counter(request: Request) -> None:
|
|
@@ -62,7 +59,7 @@ def raise_error(request: Request) -> NoReturn:
|
|
|
62
59
|
request: Request object containing client information.
|
|
63
60
|
"""
|
|
64
61
|
failed_auth_counter(request)
|
|
65
|
-
|
|
62
|
+
logger.CUSTOM_LOGGER.error(
|
|
66
63
|
"Incorrect username or password: %d",
|
|
67
64
|
models.ws_session.invalid[request.client.host],
|
|
68
65
|
)
|
|
@@ -88,16 +85,12 @@ def extract_credentials(authorization: HTTPAuthorizationCredentials) -> List[str
|
|
|
88
85
|
def verify_login(
|
|
89
86
|
authorization: HTTPAuthorizationCredentials,
|
|
90
87
|
request: Request,
|
|
91
|
-
env_username: str,
|
|
92
|
-
env_password: str,
|
|
93
88
|
) -> str | NoReturn:
|
|
94
89
|
"""Verifies authentication and generates session token for each user.
|
|
95
90
|
|
|
96
91
|
Args:
|
|
97
92
|
authorization: Authorization header from the request.
|
|
98
93
|
request: Request object containing client information.
|
|
99
|
-
env_username: Environment variable for the username.
|
|
100
|
-
env_password: Environment variable for the password.
|
|
101
94
|
|
|
102
95
|
Returns:
|
|
103
96
|
str:
|
|
@@ -107,11 +100,11 @@ def verify_login(
|
|
|
107
100
|
username, signature, timestamp = extract_credentials(authorization)
|
|
108
101
|
else:
|
|
109
102
|
raise_error(request)
|
|
110
|
-
if secrets.compare_digest(username,
|
|
111
|
-
hex_user = secure.hex_encode(
|
|
112
|
-
hex_pass = secure.hex_encode(
|
|
103
|
+
if secrets.compare_digest(username, models.env.username):
|
|
104
|
+
hex_user = secure.hex_encode(models.env.username)
|
|
105
|
+
hex_pass = secure.hex_encode(models.env.password)
|
|
113
106
|
else:
|
|
114
|
-
|
|
107
|
+
logger.CUSTOM_LOGGER.warning("User '%s' not allowed", models.env.username)
|
|
115
108
|
raise_error(request)
|
|
116
109
|
message = f"{hex_user}{hex_pass}{timestamp}"
|
|
117
110
|
expected_signature = secure.calculate_hash(message)
|
|
@@ -151,10 +144,10 @@ def verify_session(
|
|
|
151
144
|
and session_token
|
|
152
145
|
and secrets.compare_digest(session_token, stored_token)
|
|
153
146
|
):
|
|
154
|
-
|
|
147
|
+
logger.CUSTOM_LOGGER.info("Session is valid for host: %s", request.client.host)
|
|
155
148
|
return
|
|
156
149
|
elif not session_token:
|
|
157
|
-
|
|
150
|
+
logger.CUSTOM_LOGGER.warning(
|
|
158
151
|
"Session is invalid or expired for host: %s", request.client.host
|
|
159
152
|
)
|
|
160
153
|
raise models.RedirectException(
|
|
@@ -162,7 +155,7 @@ def verify_session(
|
|
|
162
155
|
destination=enums.APIEndpoints.fastapi_login,
|
|
163
156
|
)
|
|
164
157
|
else:
|
|
165
|
-
|
|
158
|
+
logger.CUSTOM_LOGGER.warning(
|
|
166
159
|
"Session token mismatch for host: %s. Expected: %s, Received: %s",
|
|
167
160
|
request.client.host,
|
|
168
161
|
stored_token,
|
|
@@ -198,6 +191,6 @@ def clear_session(host: str) -> None:
|
|
|
198
191
|
"""
|
|
199
192
|
if models.ws_session.client_auth.get(host):
|
|
200
193
|
models.ws_session.client_auth.pop(host)
|
|
201
|
-
|
|
194
|
+
logger.CUSTOM_LOGGER.info("Session cleared for host: %s", host)
|
|
202
195
|
else:
|
|
203
|
-
|
|
196
|
+
logger.CUSTOM_LOGGER.warning("No session found for host: %s", host)
|
uiauth/version.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
version = "0.2.1"
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
fastapi_ui_auth-0.1.1.dist-info/licenses/LICENSE,sha256=_sOIKJWdD2o1WwwDIwYB2qTP2nlSWqT5Tyg9jr1Xa4w,1070
|
|
2
|
-
fastapiauthenticator/__init__.py,sha256=H1tJUJp4FWweEu2JeMw5vmqjtMhMwR4kUd11ek8UmwQ,320
|
|
3
|
-
fastapiauthenticator/endpoints.py,sha256=PF2qu6XQ3MQStFEprRYYxiS6Dl6ukYaMqEkSlO-F3Ls,2104
|
|
4
|
-
fastapiauthenticator/enums.py,sha256=WO0eBv3l9HHr1I_ZXtAifCgdL-db_tZj9ka7jnjiS5k,547
|
|
5
|
-
fastapiauthenticator/models.py,sha256=GxmQfSvg70OTsvswJ3QFq_lxq-Yz1fIfzW6x8d4Sj40,2726
|
|
6
|
-
fastapiauthenticator/secure.py,sha256=ZOH6kT4BD56VqwaKdKocX7eSE8tqZcu-tK0QOmjY58k,1089
|
|
7
|
-
fastapiauthenticator/service.py,sha256=7E474kwPIDsbtZPMGJTSo0thKc2FP2n-MUogsH0ZrZI,6247
|
|
8
|
-
fastapiauthenticator/utils.py,sha256=Is7oaaHYsZ97aPmDWTcIqStW693HRIDUKtkGBdfyMVg,6731
|
|
9
|
-
fastapiauthenticator/version.py,sha256=I4WU5JcKZjfggPiNIoGDFxUDB05Ym1xFhbC3J_MjilA,18
|
|
10
|
-
fastapiauthenticator/templates/index.html,sha256=mA2R6gk6lvibq_AmPgGHBFQijYtNUD7IIfeBSJWQrM4,9078
|
|
11
|
-
fastapiauthenticator/templates/session.html,sha256=LUCvcEdQOjfIXjRZ2gPx2s5wyzNuCve4OMge0hXaBLM,3053
|
|
12
|
-
fastapiauthenticator/templates/unauthorized.html,sha256=UZo1Jt64-CFfjwWTGicUMdHVWkYkXCJBRxvit4QTiQM,3015
|
|
13
|
-
fastapi_ui_auth-0.1.1.dist-info/METADATA,sha256=cmKRN9LBPNgKnm66iKXZaAkBasddHo2vs5ISjFw9HJE,2733
|
|
14
|
-
fastapi_ui_auth-0.1.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
15
|
-
fastapi_ui_auth-0.1.1.dist-info/top_level.txt,sha256=EpDRP7uLM0f-Vd5rUtLBh4MTMAnpXzw1pr0DSknW_Ds,21
|
|
16
|
-
fastapi_ui_auth-0.1.1.dist-info/RECORD,,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
fastapiauthenticator
|
fastapiauthenticator/__init__.py
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
from fastapiauthenticator.enums import APIEndpoints, APIMethods # noqa: F401,E402
|
|
2
|
-
from fastapiauthenticator.models import Parameters # noqa: F401,E402
|
|
3
|
-
from fastapiauthenticator.service import Authenticator # noqa: F401,E402
|
|
4
|
-
from fastapiauthenticator.version import version # noqa: F401,E402
|
|
5
|
-
|
|
6
|
-
protect = Authenticator
|
fastapiauthenticator/version.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
version = "0.1.1"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|