From 1d47c53f496b975dfec9825aaa42cbfe2a33218a Mon Sep 17 00:00:00 2001 From: realityinspector Date: Wed, 11 Mar 2026 13:21:05 -0600 Subject: [PATCH] feat(api): gate OpenAPI docs behind API key auth Disable default /docs, /redoc, /openapi.json endpoints and re-mount them behind get_current_user auth dependency so API schema is not publicly exposed. --- api/main.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/api/main.py b/api/main.py index 9d68a0b..9e6f9cd 100644 --- a/api/main.py +++ b/api/main.py @@ -18,9 +18,10 @@ from contextlib import asynccontextmanager from datetime import datetime -from fastapi import FastAPI, HTTPException, Request +from fastapi import Depends, FastAPI, HTTPException, Request from fastapi.middleware.cors import CORSMiddleware -from fastapi.responses import JSONResponse +from fastapi.openapi.docs import get_redoc_html, get_swagger_ui_html +from fastapi.responses import HTMLResponse, JSONResponse from slowapi.errors import RateLimitExceeded from .deps import ( @@ -135,6 +136,9 @@ def create_app( """, lifespan=lifespan, debug=debug or settings.debug, + docs_url=None, + redoc_url=None, + openapi_url=None, ) # Add CORS middleware @@ -195,6 +199,21 @@ async def general_exception_handler(request: Request, exc: Exception): ).model_dump(), ) + # Auth-gated OpenAPI docs + from .auth import get_current_user + + @application.get("/openapi.json", include_in_schema=False) + async def openapi_schema(_user: str = Depends(get_current_user)): + return JSONResponse(application.openapi()) + + @application.get("/docs", include_in_schema=False, response_class=HTMLResponse) + async def swagger_ui(_user: str = Depends(get_current_user)): + return get_swagger_ui_html(openapi_url="/openapi.json", title=f"{application.title} - Docs") + + @application.get("/redoc", include_in_schema=False, response_class=HTMLResponse) + async def redoc_ui(_user: str = Depends(get_current_user)): + return get_redoc_html(openapi_url="/openapi.json", title=f"{application.title} - ReDoc") + # Add root endpoints @application.get("/", include_in_schema=False) async def root():