prototype for authentication

This commit is contained in:
2026-05-08 12:16:41 +02:00
parent 01ac806b62
commit 1634994044
5 changed files with 156 additions and 0 deletions
View File
+56
View File
@@ -0,0 +1,56 @@
from fastapi.responses import RedirectResponse
from nicegui import APIRouter, app, ui
#TODO
passwords = {"a": "a", "b": "b", "c": "c"}
pgroups = {"a": {"p11111", "p22222"}, "b": {"p33333", "p44444"}}
router = APIRouter()
@router.page("/login")
def login(redirect_to: str = "/") -> RedirectResponse | None:
if app.storage.user.get("authenticated", False):
return RedirectResponse("/")
def try_login(): # local function to avoid passing username and password as arguments
if not username.value:
ui.notify("Missing username", color="negative")
focus(username)
return
if not password.value:
focus(password)
return
if passwords.get(username.value) != password.value:
ui.notify("Wrong username or password", color="negative")
return
app.storage.user.update(
username=username.value,
authenticated=True,
pgroups=list(pgroups.get(username.value, set()))
)
ui.navigate.to(redirect_to) # go back to where the user wanted to go
with ui.card().classes("absolute-center items-stretch"):
ui.image("icon.png")
username = ui.input("Username").props("autofocus")
password = ui.input("Password", password=True, password_toggle_button=True)
username.on("keydown.enter", try_login)
password.on("keydown.enter", try_login)
ui.button("log in", icon="login", on_click=try_login)
return None
def focus(element):
ui.run_javascript(f"getElement({element.id}).$refs.qRef.focus()")
+9
View File
@@ -0,0 +1,9 @@
from nicegui import app, ui
def logout():
app.storage.user.clear()
ui.navigate.to("/login")
+29
View File
@@ -0,0 +1,29 @@
from fastapi import Request
from fastapi.responses import RedirectResponse
from nicegui import app
from starlette.middleware.base import BaseHTTPMiddleware
unrestricted_page_routes = {"/login"}
class AuthMiddleware(BaseHTTPMiddleware):
"""
This middleware restricts access to all NiceGUI pages.
It redirects the user to the login page if they are not authenticated.
"""
async def dispatch(self, request: Request, call_next):
path = request.url.path
if (
app.storage.user.get("authenticated", False)
or path in unrestricted_page_routes
or path.startswith("/_nicegui")
):
return await call_next(request)
return RedirectResponse(f"/login?redirect_to={path}")
+62
View File
@@ -0,0 +1,62 @@
#!/usr/bin/env python3
from nicegui import app, ui
from auth.login import router as login_router
from auth.logout import logout
from auth.mw import AuthMiddleware
app.include_router(login_router)
app.add_middleware(AuthMiddleware)
@ui.page("/")
def main_page():
with ui.column().classes("absolute-center items-center"):
username = app.storage.user.get("username", "unknown user")
ui.label(f"Hello {username}!").classes("text-2xl")
ui.button("log out", icon="logout", on_click=logout)
pgroups = app.storage.user.get("pgroups", set())
ui.select(
label="pgroup",
options=sorted(pgroups),
with_input=True,
on_change=lambda e: ui.navigate.to(e.value)
)
#TODO: the below is a dummy
from typing import Annotated
from fastapi import Path
PGroup = Annotated[str, Path(pattern=r"^p\d{5}$")]
@ui.page("/{pgroup}")
def table(pgroup: PGroup):
pgroups = app.storage.user.get("pgroups", set())
if pgroup in pgroups:
table_show(pgroup)
else:
table_deny(pgroup)
def table_show(pgroup):
with ui.column().classes("absolute-center items-center gap-8"):
ui.icon("sym_o_thumb_up", size="xl")
ui.label(f"{pgroup} erlaubt!").classes("text-2xl")
def table_deny(pgroup):
with ui.column().classes("absolute-center items-center gap-8"):
ui.icon("sym_o_front_hand", size="xl")
ui.label(f"{pgroup} verboten!").classes("text-2xl")
ui.run(storage_secret="THIS_NEEDS_TO_BE_CHANGED", dark=True) #TODO: storage_secret?