Checkpoint, fixing reset token handling broken in earlier edits.
This commit is contained in:
@@ -24,7 +24,7 @@ def main():
|
||||
host=args.host,
|
||||
port=args.port,
|
||||
reload=args.dev,
|
||||
log_level="debug" if args.dev else "info",
|
||||
log_level="info",
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -10,11 +10,13 @@ This module contains all the HTTP API endpoints for:
|
||||
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import Cookie, Depends, FastAPI, Request, Response
|
||||
from fastapi import Cookie, Depends, FastAPI, Response
|
||||
from fastapi.security import HTTPBearer
|
||||
|
||||
from passkey.util import passphrase
|
||||
|
||||
from .. import aaguid
|
||||
from ..authsession import delete_credential, get_session
|
||||
from ..authsession import delete_credential, get_reset, get_session
|
||||
from ..db import db
|
||||
from ..util.tokens import session_key
|
||||
from . import session
|
||||
@@ -26,7 +28,7 @@ def register_api_routes(app: FastAPI):
|
||||
"""Register all API routes on the FastAPI app."""
|
||||
|
||||
@app.post("/auth/validate")
|
||||
async def validate_token(request: Request, response: Response, auth=Cookie(None)):
|
||||
async def validate_token(auth=Cookie(None)):
|
||||
"""Lightweight token validation endpoint."""
|
||||
try:
|
||||
s = await get_session(auth)
|
||||
@@ -39,11 +41,12 @@ def register_api_routes(app: FastAPI):
|
||||
return {"status": "error", "valid": False}
|
||||
|
||||
@app.post("/auth/user-info")
|
||||
async def api_user_info(auth=Cookie(None)):
|
||||
async def api_user_info(response: Response, auth=Cookie(None)):
|
||||
"""Get full user information for the authenticated user."""
|
||||
try:
|
||||
s = await get_session(auth, reset_allowed=True)
|
||||
u = await db.instance.get_user_by_user_uuid(s.user_uuid)
|
||||
reset = passphrase.is_well_formed(auth)
|
||||
s = await (get_reset if reset else get_session)(auth)
|
||||
u = await db.instance.get_user_by_uuid(s.user_uuid)
|
||||
# Get all credentials for the user
|
||||
credential_ids = await db.instance.get_credentials_by_user_uuid(s.user_uuid)
|
||||
|
||||
@@ -82,10 +85,11 @@ def register_api_routes(app: FastAPI):
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"authenticated": not reset,
|
||||
"session_type": s.info["type"],
|
||||
"user": {
|
||||
"user_uuid": str(u.user_uuid),
|
||||
"user_name": u.user_name,
|
||||
"user_uuid": str(u.uuid),
|
||||
"user_name": u.display_name,
|
||||
"created_at": u.created_at.isoformat() if u.created_at else None,
|
||||
"last_seen": u.last_seen.isoformat() if u.last_seen else None,
|
||||
"visits": u.visits,
|
||||
@@ -94,8 +98,11 @@ def register_api_routes(app: FastAPI):
|
||||
"aaguid_info": aaguid_info,
|
||||
}
|
||||
except ValueError as e:
|
||||
response.status_code = 400
|
||||
return {"error": f"Failed to get user info: {e}"}
|
||||
except Exception:
|
||||
response.status_code = 500
|
||||
|
||||
return {"error": "Failed to get user info"}
|
||||
|
||||
@app.post("/auth/logout")
|
||||
@@ -103,7 +110,11 @@ def register_api_routes(app: FastAPI):
|
||||
"""Log out the current user by clearing the session cookie and deleting from database."""
|
||||
if not auth:
|
||||
return {"status": "success", "message": "Already logged out"}
|
||||
await db.instance.delete_session(session_key(auth))
|
||||
# Remove from database if possible
|
||||
try:
|
||||
await db.instance.delete_session(session_key(auth))
|
||||
except Exception:
|
||||
...
|
||||
response.delete_cookie("auth")
|
||||
return {"status": "success", "message": "Logged out successfully"}
|
||||
|
||||
@@ -123,18 +134,24 @@ def register_api_routes(app: FastAPI):
|
||||
}
|
||||
|
||||
except ValueError as e:
|
||||
response.status_code = 400
|
||||
return {"error": str(e)}
|
||||
except Exception as e:
|
||||
return {"error": f"Failed to set session: {e}"}
|
||||
except Exception:
|
||||
response.status_code = 500
|
||||
return {"error": "Failed to set session"}
|
||||
|
||||
@app.delete("/auth/credential/{uuid}")
|
||||
async def api_delete_credential(uuid: UUID, auth: str = Cookie(None)):
|
||||
async def api_delete_credential(
|
||||
response: Response, uuid: UUID, auth: str = Cookie(None)
|
||||
):
|
||||
"""Delete a specific credential for the current user."""
|
||||
try:
|
||||
await delete_credential(uuid, auth)
|
||||
return {"status": "success", "message": "Credential deleted successfully"}
|
||||
|
||||
except ValueError as e:
|
||||
response.status_code = 400
|
||||
return {"error": str(e)}
|
||||
except Exception:
|
||||
response.status_code = 500
|
||||
return {"error": "Failed to delete credential"}
|
||||
|
||||
@@ -3,9 +3,7 @@ from contextlib import asynccontextmanager
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import Cookie, FastAPI, Request, Response
|
||||
from fastapi.responses import (
|
||||
FileResponse,
|
||||
)
|
||||
from fastapi.responses import FileResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
from ..authsession import get_session
|
||||
@@ -35,24 +33,21 @@ app = FastAPI(lifespan=lifespan)
|
||||
# Mount the WebSocket subapp
|
||||
app.mount("/auth/ws", ws.app)
|
||||
|
||||
# Register API routes
|
||||
register_api_routes(app)
|
||||
register_reset_routes(app)
|
||||
|
||||
|
||||
@app.get("/auth/forward-auth")
|
||||
async def forward_authentication(request: Request, auth=Cookie(None)):
|
||||
"""A validation endpoint to use with Caddy forward_auth or Nginx auth_request."""
|
||||
with contextlib.suppress(ValueError):
|
||||
s = await get_session(auth)
|
||||
# If authenticated, return a success response
|
||||
if s.info and s.info["type"] == "authenticated":
|
||||
return Response(
|
||||
status_code=204,
|
||||
headers={
|
||||
"x-auth-user-uuid": str(s.user_uuid),
|
||||
},
|
||||
)
|
||||
if auth:
|
||||
with contextlib.suppress(ValueError):
|
||||
s = await get_session(auth)
|
||||
# If authenticated, return a success response
|
||||
if s.info and s.info["type"] == "authenticated":
|
||||
return Response(
|
||||
status_code=204,
|
||||
headers={
|
||||
"x-auth-user-uuid": str(s.user_uuid),
|
||||
},
|
||||
)
|
||||
|
||||
# Serve the index.html of the authentication app if not authenticated
|
||||
return FileResponse(
|
||||
@@ -72,3 +67,8 @@ app.mount(
|
||||
async def redirect_to_index():
|
||||
"""Serve the main authentication app."""
|
||||
return FileResponse(STATIC_DIR / "index.html")
|
||||
|
||||
|
||||
# Register API routes
|
||||
register_api_routes(app)
|
||||
register_reset_routes(app)
|
||||
|
||||
@@ -97,30 +97,39 @@ async def websocket_register_new(
|
||||
@app.websocket("/add_credential")
|
||||
async def websocket_register_add(ws: WebSocket, auth=Cookie(None)):
|
||||
"""Register a new credential for an existing user."""
|
||||
print(auth)
|
||||
await ws.accept()
|
||||
origin = ws.headers.get("origin")
|
||||
origin = ws.headers["origin"]
|
||||
try:
|
||||
s = await get_session(auth, reset_allowed=True)
|
||||
user_uuid = s.user_uuid
|
||||
|
||||
# Get user information to get the user_name
|
||||
user = await db.instance.get_user_by_user_uuid(user_uuid)
|
||||
user_name = user.user_name
|
||||
user = await db.instance.get_user_by_uuid(user_uuid)
|
||||
user_name = user.display_name
|
||||
challenge_ids = await db.instance.get_credentials_by_user_uuid(user_uuid)
|
||||
|
||||
# WebAuthn registration
|
||||
credential = await register_chat(
|
||||
ws, user_uuid, user_name, challenge_ids, origin
|
||||
)
|
||||
if s.info["type"] == "authenticated":
|
||||
token = auth
|
||||
else:
|
||||
# Replace reset session with a new session
|
||||
await db.instance.delete_session(s.key)
|
||||
token = await create_session(
|
||||
user_uuid, credential.uuid, infodict(ws, "authenticated")
|
||||
)
|
||||
assert isinstance(token, str) and len(token) == 16
|
||||
# Store the new credential in the database
|
||||
await db.instance.create_credential(credential)
|
||||
|
||||
await ws.send_json(
|
||||
{
|
||||
"status": "success",
|
||||
"user_uuid": str(user_uuid),
|
||||
"credential_id": credential.credential_id.hex(),
|
||||
"user_uuid": str(user.uuid),
|
||||
"credential_uuid": str(credential.uuid),
|
||||
"session_token": token,
|
||||
"message": "New credential added successfully",
|
||||
}
|
||||
)
|
||||
@@ -136,7 +145,7 @@ async def websocket_register_add(ws: WebSocket, auth=Cookie(None)):
|
||||
@app.websocket("/authenticate")
|
||||
async def websocket_authenticate(ws: WebSocket):
|
||||
await ws.accept()
|
||||
origin = ws.headers.get("origin")
|
||||
origin = ws.headers["origin"]
|
||||
try:
|
||||
options, challenge = passkey.auth_generate_options()
|
||||
await ws.send_json(options)
|
||||
|
||||
Reference in New Issue
Block a user