Remove some confusion between exchange and set-session endpoints, all using set-session now with a bearer code. Using codes in remote auth as well. Full separation of cookie and OIDC codes.

This commit is contained in:
Leo Vasanko
2026-02-16 14:26:20 +00:00
parent ebf5f6db2c
commit eece6d4a21
11 changed files with 257 additions and 134 deletions
+24 -37
View File
@@ -3,7 +3,6 @@ from contextlib import suppress
from datetime import UTC, datetime, timedelta
from fastapi import (
Body,
Depends,
FastAPI,
HTTPException,
@@ -68,34 +67,6 @@ async def general_exception_handler(
return JSONResponse(status_code=500, content={"detail": "Internal server error"})
@app.post("/exchange")
async def exchange_code(
request: Request,
response: Response,
code: str = Body(..., embed=True),
):
"""Exchange a session code for setting the session cookie.
Called by frontend after WebSocket authentication.
The code is ephemeral (60s TTL) and can only be used once.
"""
auth_code = authcode.codes.pop(code, None)
if not auth_code:
raise HTTPException(status_code=400, detail="Invalid or expired code")
secret = auth_code.session_key
# Verify the session exists
host = hostutil.normalize_host(request.headers.get("host", ""))
ctx = db.data().session_ctx(secret, host)
if not ctx:
raise HTTPException(status_code=400, detail="Session not found")
# Set the session cookie
session.set_session_cookie(response, secret)
return {"status": "ok", "user": str(ctx.user.uuid)}
@app.post("/validate")
async def validate_token(
request: Request,
@@ -294,13 +265,29 @@ async def api_logout(request: Request, response: Response, auth=AUTH_COOKIE):
async def api_set_session(
request: Request, response: Response, auth=Depends(bearer_auth)
):
"""Exchange an auth code for setting the session cookie.
Called by frontend after WebSocket authentication.
The code is ephemeral (60s TTL) and can only be used once.
"""
if not auth or not auth.credentials:
raise HTTPException(401, "Bearer token required")
ctx = db.data().session_ctx(auth.credentials, request.headers.get("host"))
raise HTTPException(400, "Bearer token required")
# Verify host is provided
host = hostutil.normalize_host(request.headers.get("host", ""))
if not host:
raise HTTPException(400, "Host header required")
a = authcode.consume_cookie(auth.credentials)
if not a:
raise HTTPException(401, "Code expired or already used")
secret = a.session_key
# Verify the session exists
ctx = db.data().session_ctx(secret, host)
if not ctx:
raise HTTPException(401, "Session expired")
session.set_session_cookie(response, auth.credentials)
return {
"message": "Session cookie set successfully",
"user": str(ctx.user.uuid),
}
raise HTTPException(401, f"Session not found on {host}")
session.set_session_cookie(response, secret)
return {"status": "ok", "user": str(ctx.user.uuid)}