fix(auth): /api/me answers 200 when logged out (no console error)

The logged-out landing probed /api/me, which 401'd, and the browser logs every
non-2xx fetch as a console error regardless of how the app handles it. The
bootstrap probe now returns 200 with {authenticated:false} via a new
optional_current_user dependency; api.me() maps that to null. The render gate
treats no-data as 'signed out' -> the landing, and a thrown error as a real
network/5xx failure. Other protected endpoints still 401, so mid-session expiry
is still caught by the global handler. Lighthouse (dev): Best Practices 96->100.
This commit is contained in:
npeter83 2026-07-04 18:17:24 +02:00
parent 2a8d5c0a1e
commit 603cc9854c
4 changed files with 36 additions and 9 deletions

View file

@ -719,6 +719,18 @@ def current_user(request: Request, db: Session = Depends(get_db)) -> User:
return user
def optional_current_user(
request: Request, db: Session = Depends(get_db)
) -> User | None:
"""Like `current_user` but returns None instead of raising 401 when there's no valid session.
Used by the bootstrap /api/me probe so the logged-out landing doesn't log a failed request to
the browser console (a non-2xx fetch is a console error no matter how the app handles it)."""
try:
return current_user(request, db)
except HTTPException:
return None
def require_human(user: User = Depends(current_user)) -> User:
"""Reject the shared demo account from actions that need a real Google/YouTube identity
or spend the shared quota. Most YouTube paths are already closed to it implicitly (it has

View file

@ -9,6 +9,7 @@ from app.auth import (
has_read_scope,
has_write_scope,
is_allowed,
optional_current_user,
purge_user,
)
from app.db import get_db
@ -67,8 +68,13 @@ def switch_account(
@router.get("")
def get_me(
user: User = Depends(current_user), db: Session = Depends(get_db)
user: User | None = Depends(optional_current_user), db: Session = Depends(get_db)
) -> dict:
# The app's bootstrap probe: return 200 with `authenticated: False` when logged out (rather
# than 401) so the public landing never logs a failed /api/me to the browser console. Other
# protected endpoints still 401, so mid-session expiry is still caught by the global handler.
if user is None:
return {"authenticated": False}
pending_invites = 0
if user.role == "admin":
pending_invites = (
@ -80,6 +86,7 @@ def get_me(
or 0
)
return {
"authenticated": True,
"id": user.id,
"email": user.email,
"display_name": user.display_name,