odysseus-dev/odysseus · warning · HTTPException

Authentication required

Error message

Authentication required

What it means

403 raised by GET /api/documents/{session_id} when get_current_user(request) returns no user and authentication is not disabled (i.e. the app is running with auth enabled). It is an explicit gate: anonymous callers may only list session documents when the deployment intentionally runs auth-free.

Source

Thrown at routes/document/document_routes.py:452

                "total": total,
                "languages": languages,
                "session_count": session_count,
            }
        except Exception as e:
            logger.error(f"Failed to fetch document library: {e}")
            raise HTTPException(500, f"Failed to fetch document library: {e}")
        finally:
            db.close()

    # ---- GET /api/documents/{session_id} ----
    @router.get("/api/documents/{session_id}")
    async def list_documents(request: Request, session_id: str) -> List[Dict[str, Any]]:
        user = get_current_user(request)
        db = SessionLocal()
        try:
            if not user:
                if not _auth_disabled():
                    raise HTTPException(403, "Authentication required")
            # v2 review HIGH-9: raise 403 explicitly when the caller
            # can't see this session, instead of returning [] which the
            # UI treats identically to "no docs" and silently masks
            # auth failures.
            _get_session_or_404(db, session_id, user)
            q = db.query(Document).filter(
                Document.session_id == session_id
            )
            if user:
                q = q.filter(or_(Document.owner == user, Document.owner.is_(None)))
            docs = q.order_by(Document.created_at.desc()).all()
            return [_doc_to_dict(d) for d in docs]
        finally:
            db.close()

    # ---- GET /api/document/{doc_id} ----
    @router.get("/api/document/{doc_id}")
    async def get_document(request: Request, doc_id: str) -> Dict[str, Any]:

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Send valid credentials with the request (session cookie or auth header) as the app expects.
  2. If this deployment is intentionally auth-free, enable the app's auth-disabled mode so _auth_disabled() returns true.
  3. Check CORS/proxy config so cookies survive the hop (withCredentials, same-site settings).
  4. Re-login to obtain a fresh token if the old one expired or the user was recreated.

Example fix

// before
fetch(`/api/documents/${sessionId}`)  // no credentials -> 403
// after
fetch(`/api/documents/${sessionId}`, { credentials: 'include' })
Defensive patterns

Strategy: validation

Validate before calling

async function listDocs(sessionId) {
  if (!auth.currentUser()) return redirect('/login?next=' + encodeURIComponent(location.pathname));
  return api.get(`/api/documents/${sessionId}`, { credentials: 'include' });
}

Type guard

function hasUser(): boolean { return Boolean(auth.currentUser()); }

Try / catch

try { await api.get(url); }
catch (e) { if (e.status === 403 && e.message === 'Authentication required') { await auth.refreshOrLogin(); return api.get(url, { credentials: 'include' }); } throw e; }

Prevention

When it happens

Trigger: Request arrives without valid auth credentials (missing/expired session cookie or token); auth enabled in config while the client assumed open access; a proxy strips the auth header/cookie; the user record was deleted so the token no longer resolves.

Common situations: Frontend dev server not forwarding cookies (different port/domain without CORS credentials); token expiry after a long idle period; environment flipped from AUTH_DISABLED=1 to auth-enabled between environments; logged-in user removed from the user store.

Understand the failure class

Related errors


AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14). Data as JSON: /api/errors/01afc69d34a9d337. Report an issue: GitHub.