feat: add web login handler — form POST sets cookie and redirects to dashboard
This commit is contained in:
parent
d49844c1fe
commit
5ae13b199c
2 changed files with 32 additions and 2 deletions
|
|
@ -3,7 +3,8 @@
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException
|
from fastapi import APIRouter, Depends, Form, HTTPException
|
||||||
|
from fastapi.responses import RedirectResponse
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
import duckdb
|
import duckdb
|
||||||
from argon2 import PasswordHasher
|
from argon2 import PasswordHasher
|
||||||
|
|
@ -57,6 +58,35 @@ async def password_login(
|
||||||
return {"access_token": token, "token_type": "bearer", "email": user["email"], "role": user["role"]}
|
return {"access_token": token, "token_type": "bearer", "email": user["email"], "role": user["role"]}
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/login/web")
|
||||||
|
async def password_login_web(
|
||||||
|
email: str = Form(...),
|
||||||
|
password: str = Form(""),
|
||||||
|
conn: duckdb.DuckDBPyConnection = Depends(_get_db),
|
||||||
|
):
|
||||||
|
"""Web form login — sets cookie and redirects to dashboard."""
|
||||||
|
repo = UserRepository(conn)
|
||||||
|
user = repo.get_by_email(email)
|
||||||
|
if not user or not user.get("password_hash"):
|
||||||
|
return RedirectResponse(url="/login/password?error=invalid", status_code=302)
|
||||||
|
|
||||||
|
try:
|
||||||
|
ph = PasswordHasher()
|
||||||
|
ph.verify(user["password_hash"], password)
|
||||||
|
except (VerifyMismatchError, Exception):
|
||||||
|
return RedirectResponse(url="/login/password?error=invalid", status_code=302)
|
||||||
|
|
||||||
|
token = create_access_token(user["id"], user["email"], user["role"])
|
||||||
|
is_production = os.environ.get("TESTING", "").lower() not in ("1", "true")
|
||||||
|
response = RedirectResponse(url="/dashboard", status_code=302)
|
||||||
|
response.set_cookie(
|
||||||
|
key="access_token", value=token,
|
||||||
|
httponly=True, max_age=86400, samesite="lax",
|
||||||
|
secure=is_production,
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
@router.post("/setup")
|
@router.post("/setup")
|
||||||
async def password_setup(
|
async def password_setup(
|
||||||
request: PasswordSetupRequest,
|
request: PasswordSetupRequest,
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
<!-- Sign In Tab -->
|
<!-- Sign In Tab -->
|
||||||
<div id="signin-tab" class="auth-tab-content active">
|
<div id="signin-tab" class="auth-tab-content active">
|
||||||
<form method="POST" action="/auth/password/login" class="login-form">
|
<form method="POST" action="/auth/password/login/web" class="login-form">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="email-signin">Email Address</label>
|
<label for="email-signin">Email Address</label>
|
||||||
<input type="email"
|
<input type="email"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue