implement autologin on login page

This commit is contained in:
kittentm 2026-02-06 23:06:27 +01:00
commit b9beb11268
5 changed files with 99 additions and 25 deletions

View file

@ -5,9 +5,10 @@ class Settings(BaseSettings):
port: int = 5000
db_url: str
fernet_key: str
cookie_httponly: bool = True
cookie_httponly: bool = False
frontend_url: str
model_config = SettingsConfigDict(env_file=".env", extra="ignore")
cookie_secure: bool = False
settings = Settings()
cipher = Fernet(settings.fernet_key.encode())

View file

@ -1 +1,4 @@
APP_PORT=5000
FERNET_KEY=
DB_URL=
PORT=5000
FRONTEND_URL=

25
main.py
View file

@ -1,14 +1,31 @@
from fastapi import FastAPI
from routes import sso
from database import init_db
from fastapi import FastAPI, Request
from routes import sso, user
from config import settings
import uvicorn
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
init_db()
app.add_middleware(
CORSMiddleware,
allow_origins=[settings.frontend_url],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
expose_headers=["Set-Cookie"],
)
@app.middleware("http")
async def force_cors_on_errors(request: Request, call_next):
response = await call_next(request)
origin = request.headers.get("origin")
if origin == settings.frontend_url:
response.headers["Access-Control-Allow-Origin"] = origin
response.headers["Access-Control-Allow-Credentials"] = "true"
return response
app.include_router(sso.router, prefix="/api/v2/sso")
app.include_router(user.router, prefix="/api/v1/users")
if __name__ == '__main__':
uvicorn.run("main:app", host="0.0.0.0", port=settings.port, reload=True)

View file

@ -4,6 +4,7 @@ from sqlalchemy.orm import Session as DBSession
from database import SessionLocal, User, Session
from config import settings, cipher
from services import auth
from typing import Optional
import uuid
router = APIRouter()
@ -18,56 +19,75 @@ def get_db():
@router.post('/spfn/generate_token')
async def login(
request: Request,
username: str = Form(...),
password: str = Form(...),
frontend_origin: str = Form(None),
username: Optional[str] = Form(None),
password: Optional[str] = Form(None),
frontend_origin: Optional[str] = Form(None),
rememberMe: bool = Form(False),
db: DBSession = Depends(get_db)
):
host = (frontend_origin or request.headers.get("referer") or "/").rstrip('/')
sep = "&" if "?" in host else "?"
auth_path = f"{host}/users/auth/splatfestival/"
print(f"host: {host}")
if not username or not password:
print("blank fields detected")
return RedirectResponse(f"{auth_path}{sep}error=auth&username={username or ''}", 303)
try:
print("fetching spfn token")
data = auth.get_token(username, password)
if not data or "token" not in data:
print("auth failed")
return RedirectResponse(f"{host}/login?error=auth", 303)
print("encrypting password")
enc_pass = cipher.encrypt(password.encode()).decode()
return RedirectResponse(f"{auth_path}{sep}error=auth&username={username}", 303)
print(f"checking user {username}")
user = db.query(User).filter(User.username == username).first()
if not user:
print("creating new user")
print("creating new user with argon2id")
enc_pass = cipher.encrypt(password.encode()).decode() if rememberMe else ""
user = User(
username=username,
local_hash=auth.hash_password(password),
spfn_pass_enc=enc_pass
)
db.add(user)
elif rememberMe:
print("updating encrypted pass for client")
user.spfn_pass_enc = cipher.encrypt(password.encode()).decode()
else:
print("updating user")
user.spfn_pass_enc = enc_pass
print("client requested no password storage, skipping update")
db.flush()
print("creating session")
new_session = Session(username=username, remember_me=rememberMe)
db.add(new_session)
print("checking for existing session")
active_session = db.query(Session).filter(Session.username == username).first()
if active_session:
print(f"refreshing session for {username}")
active_session.remember_me = rememberMe
else:
print(f"creating new session for {username}")
active_session = Session(username=username, remember_me=rememberMe)
db.add(active_session)
db.commit()
print("setting cookie and redirecting")
print("setting client-based cookie")
response = RedirectResponse(url=f"{host}/friend_list/", status_code=303)
cookie_age = 2592000 if rememberMe else None
#you are a pain in the ass respectfully
response.set_cookie(
key="session_id",
value=new_session.id,
value=active_session.id,
httponly=settings.cookie_httponly,
secure=True,
secure=False,
samesite="lax",
max_age=cookie_age
path="/",
max_age=cookie_age,
domain=None
)
return response

33
routes/user.py Normal file
View file

@ -0,0 +1,33 @@
from fastapi import APIRouter, Request, Depends, HTTPException
from sqlalchemy.orm import Session as DBSession
from database import SessionLocal, User, Session
from typing import Dict
router = APIRouter()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@router.get('/me')
async def get_current_user(request: Request, db: DBSession = Depends(get_db)):
session_id = request.cookies.get("session_id")
if not session_id:
raise HTTPException(status_code=401, detail="No session cookie")
active_session = db.query(Session).filter(Session.id == session_id).first()
if not active_session:
print(f"invalid or expired session: {session_id}")
raise HTTPException(status_code=401, detail="invalid session")
user = db.query(User).filter(User.username == active_session.username).first()
if not user:
print(f"session linked to non-existent user: {active_session.username}")
raise HTTPException(status_code=401, detail="user not found")
print(f"auto-login success for {user.username}")
return {
"status": "ok",
"username": user.username
}