Unauthenticated API docs and OAuth error detail leakage #69

Closed
opened 2026-07-28 05:57:14 +00:00 by claude-bot · 1 comment

Severity: MEDIUM

The bugs

Two small unauthenticated information leaks.

1. Interactive docs are public. backend/app/main.py:17 constructs
FastAPI(title="Circa API", version="0.1.0") with no docs_url=None / redoc_url=None /
openapi_url=None and no auth dependency. /docs, /redoc, and /openapi.json are reachable
without a session, publishing every route, parameter, and schema. This contradicts
docs/circa-spec.md §2.3 ("all endpoints require authentication except the auth entry/callback
flow") and hands an attacker a complete machine-readable map of the attack surface, including
the ingest endpoint's field names.

2. The OAuth callback reflects raw exception text. backend/app/api/routes/auth.py:39-42:

except Exception as exc:
    raise HTTPException(status_code=400, detail=f"OAuth error: {exc}") from exc

A broad catch interpolating the exception into a client-visible response on a
pre-authentication endpoint. Authlib exceptions can embed the token endpoint URL, provider
error payloads, and occasionally request parameters — useful for fingerprinting the Authentik
setup before attempting the aud/PKCE attacks.

Fix

  • docs_url=None if settings.environment != "development" else "/docs", same for redoc and
    openapi — or gate them behind an admin dependency.
  • Log the OAuth exception server-side with full detail; return a generic "Authentication failed"
    to the client. Catch specific Authlib exception types rather than bare Exception.

Done when

  • /docs, /redoc, /openapi.json are unreachable outside development
  • OAuth failures return a generic message and log the detail server-side
  • Tests assert the docs routes 404 in production mode

References

  • backend/app/main.py:17
  • backend/app/api/routes/auth.py:39-42
  • docs/circa-spec.md §2.3
## Severity: MEDIUM ## The bugs Two small unauthenticated information leaks. **1. Interactive docs are public.** `backend/app/main.py:17` constructs `FastAPI(title="Circa API", version="0.1.0")` with no `docs_url=None` / `redoc_url=None` / `openapi_url=None` and no auth dependency. `/docs`, `/redoc`, and `/openapi.json` are reachable without a session, publishing every route, parameter, and schema. This contradicts `docs/circa-spec.md` §2.3 ("all endpoints require authentication except the auth entry/callback flow") and hands an attacker a complete machine-readable map of the attack surface, including the ingest endpoint's field names. **2. The OAuth callback reflects raw exception text.** `backend/app/api/routes/auth.py:39-42`: ```python except Exception as exc: raise HTTPException(status_code=400, detail=f"OAuth error: {exc}") from exc ``` A broad catch interpolating the exception into a client-visible response on a **pre-authentication** endpoint. Authlib exceptions can embed the token endpoint URL, provider error payloads, and occasionally request parameters — useful for fingerprinting the Authentik setup before attempting the `aud`/PKCE attacks. ## Fix - `docs_url=None if settings.environment != "development" else "/docs"`, same for redoc and openapi — or gate them behind an admin dependency. - Log the OAuth exception server-side with full detail; return a generic "Authentication failed" to the client. Catch specific Authlib exception types rather than bare `Exception`. ## Done when - [ ] `/docs`, `/redoc`, `/openapi.json` are unreachable outside development - [ ] OAuth failures return a generic message and log the detail server-side - [ ] Tests assert the docs routes 404 in production mode ## References - `backend/app/main.py:17` - `backend/app/api/routes/auth.py:39-42` - `docs/circa-spec.md` §2.3
claude-bot added this to the v0.1.1 milestone 2026-07-28 05:57:14 +00:00
Author

Fixed in 791d117. CI green.

Docs disabled outside development. docs_url, redoc_url, and openapi_url are None unless
is_development. They previously published every route, parameter, and schema to anyone who could
reach the host — including the ingest endpoint's field names — which contradicts the spec's §2.3
"all endpoints require authentication" and removed the guesswork from every other finding.

OAuth errors no longer reflected. The callback caught broadly and interpolated the exception into
a client-visible response, on the one pre-authentication endpoint that talks to an external service.
Authlib exceptions can embed the token endpoint URL, provider error payloads, and request
parameters. It now logs the exception server-side (with exc_info) and returns a generic
"Authentication failed".

Note the broad except Exception is retained deliberately — narrowing it to specific Authlib types
risks letting an unanticipated exception propagate as a 500 with a traceback, which is the outcome
this is meant to prevent. The fix is not leaking the detail, not catching less.

**Fixed** in 791d117. CI green. **Docs disabled outside development.** `docs_url`, `redoc_url`, and `openapi_url` are `None` unless `is_development`. They previously published every route, parameter, and schema to anyone who could reach the host — including the ingest endpoint's field names — which contradicts the spec's §2.3 "all endpoints require authentication" and removed the guesswork from every other finding. **OAuth errors no longer reflected.** The callback caught broadly and interpolated the exception into a client-visible response, on the one pre-authentication endpoint that talks to an external service. Authlib exceptions can embed the token endpoint URL, provider error payloads, and request parameters. It now logs the exception server-side (with `exc_info`) and returns a generic "Authentication failed". Note the broad `except Exception` is retained deliberately — narrowing it to specific Authlib types risks letting an unanticipated exception propagate as a 500 with a traceback, which is the outcome this is meant to prevent. The fix is not leaking the detail, not catching less.
Sign in to join this conversation.
No description provided.