
Introduction
Learn how to restrict access to your FastAPI documentation endpoints with both basic and advanced authentication methods, ensuring your API PlayStation API and security.
FastAPI's automatic generation of API documentation via Swagger UI (/docs) and ReDoc (/redoc) is a powerful feature that provides interactive, auto-generated API documentation. However, exposing these endpoints publicly can risk leaking sensitive API details to unauthorized users, such as bots, competitors, or attackers. In this blog post, we'll guide you through securing your FastAPI documentation endpoints (/docs, /redoc, and /openapi.json) using both a simple HTTP Basic Authentication approach and a more robust database-backed, role-based authentication method.
Why Secure Your API Documentation?
Leaving API documentation endpoints unprotected can lead to several risks:
- Security Exposure: Public access to
/docs,/redoc, or/openapi.jsonmay reveal sensitive endpoint details or internal API structures. - API Abuse: Bots or malicious users could scrape documentation to identify vulnerabilities or misuse your API.
- Compliance Needs: Restricting access aligns with data protection regulations in regulated industries.
- Professional Control: Limiting access to authorized developers or partners maintains a secure and professional workflow.
This guide covers two approaches: a simple method using HTTP Basic Authentication with hardcoded credentials, and an advanced method with database-backed user authentication and role-based access control. We'll also address the critical issue of securing the /openapi.json endpoint to prevent unauthorized schema access.
Approach 1: Basic Authentication for Documentation Endpoints
For a quick and straightforward solution, you can use HTTP Basic Authentication to protect your documentation endpoints. This requires users to enter a username and password to access /docs and /redoc.
Step 1: Create a Dependency for Basic Authentication
First, create a dependency function to handle authentication using HTTP Basic Authentication. This function checks the provided username and password against hardcoded values.
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials
import secrets
security = HTTPBasic()
def get_current_username(credentials: HTTPBasicCredentials = Depends(security)) -> str:
correct_username = secrets.compare_digest(credentials.username, "username")
correct_password = secrets.compare_digest(credentials.password, "password")
if not (correct_username and correct_password):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect email or password",
headers={"WWW-Authenticate": "Basic"},
)
return credentials.username
Note: Hardcoding credentials ("username" and "password") is suitable for testing or small projects but is not recommended for production due to security concerns. We'll cover a more secure approach later.
Step 2: Secure the /docs and /redoc Endpoints
Use the dependency from Step 1 to secure the /docs and /redoc endpoints. When users access these endpoints, they'll be prompted for credentials.
from fastapi import APIRouter, Depends
from fastapi.responses import HTMLResponse
from fastapi.openapi.docs import get_swagger_ui_html, get_redoc_html
api_router = APIRouter()
@api_router.get("/docs", response_class=HTMLResponse)
async def get_docs(username: str = Depends(get_current_username)) -> HTMLResponse:
return get_swagger_ui_html(openapi_url="/api/openapi.json", title="docs")
@api_router.get("/redoc", response_class=HTMLResponse)
async def get_redoc(username: str = Depends(get_current_username)) -> HTMLResponse:
return get_redoc_html(openapi_url="/api/openapi.json", title="redoc")
How It Works: The get_current_username dependency triggers a login prompt. If the credentials match, users gain access to the Swagger UI or ReDoc interfaces; otherwise, a 401 Unauthorized error is returned.
Step 3: Disable Default Documentation URLs
To ensure only your custom, authenticated routes are accessible, disable FastAPI's default documentation endpoints by setting docs_url, redoc_url, and openapi_url to None.
from fastapi import FastAPI
from fastapi.responses import UJSONResponse
import metadata
app = FastAPI(
title="FastAPI",
version=metadata.version("FastAPI"),
docs_url=None,
redoc_url=None,
openapi_url="/api/openapi.json",
default_response_class=UJSONResponse,
)
app.include_router(api_router, prefix="")
Important: This setup still leaves /api/openapi.json exposed, which is a security risk. We'll address this in the advanced approach below.
Approach 2: Advanced Authentication with Database and Role-Based Access
For production environments, a more secure approach uses a database to store user credentials and implements role-based access control to restrict documentation access to users with a specific role (e.g., "developer"). This method integrates with holster with SQLAlchemy and avoids hardcoded credentials.
Step 1: Create an Advanced Authentication Dependency
This dependency authenticates users against a database and checks for the "developer" role, with special handling for local development environments.
from typing import Optional
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from sqlalchemy.orm import joinedload
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from app.core.config import settings
from app.core.loggers import app_logger as logger
from app.models.users import User
from diffuser.utils.password_util import verify_password
from diffuser.database.get_session import get_async_session
from diffuser.utils.responses import not_authorized_response
from app.models.roles import Role
security = HTTPBasic()
def has_role(user: User, role_name: str) -> bool:
"""Check if the user has a specific role."""
return any(role.name.lower() == role_name.lower() for role in user.roles)
async def get_current_docs_user(
credentials: HTTPBasicCredentials = Depends(security),
db: AsyncSession = Depends(get_async_session),
) -> User:
if settings.ENV == "local":
return User(
email=f"developer@{settings.DOMAIN}",
password=hash_password("developer"),
roles=[
Role(
name="developer",
label="Developer",
description="Developer role",
)
],
)
stmt = (
select(User)
.options(joinedload(User.roles))
.where(User.email == credentials.username.lower())
)
user: Optional[User] = await user_crud.get(
db=db,
statement=stmt,
)
if not user:
logger.error(f"Incorrect username or password for docs: {credentials.username}")
return not_authorized_response(
message="Incorrect username or password",
headers={"WWW-Authenticate": "Basic"},
)
if not verify_password(credentials.password, user.password):
logger.error(f"Incorrect password for docs")
return not_authorized_response(
message="Incorrect password",
headers={"WWW-Authenticate": "Basic"},
)
if not has_role(user, "developer"):
logger.error(f"User {user.email} does not have developer role")
return not_authorized_response(
message="User does not have developer role",
headers={"WWW-Authenticate": "Basic"},
)
logger.info(f"Authenticated developer user: {user.email}")
return user
Key Features:
- Database Authentication: Validates credentials against a database using SQLAlchemy.
- Role-Based Access: Restricts access to users with the "developer" role.
- Local Development Support: Allows easy access in local environments with default credentials.
- Secure Passwords: Uses hashed passwords for secure storage.
- Logging: Logs authentication attempts for monitoring and security.
Step 2: Secure All Documentation Endpoints
Create custom routes for /docs, /redoc, and /openapi.json, applying the advanced authentication dependency.
from fastapi import APIRouter, Depends
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.openapi.docs import get_swagger_ui_html, get_redoc_html
from fastapi.openapi.utils import get_openapi
from app.deps.docs import get_current_docs_user
from app.core.config import settings
docs_router = APIRouter()
@docs_router.get(
"/docs",
response_class=HTMLResponse,
include_in_schema=False,
summary="Swagger UI",
description="Protected Swagger UI for interacting with the API documentation.",
)
async def get_docs(username: str = Depends(get_current_docs_user)):
return get_swagger_ui_html(openapi_url="/openapi.json", title="API Documentation")
@docs_router.get(
"/redoc",
response_class=HTMLResponse,
include_in_schema=False,
summary="ReDoc UI",
description="Protected ReDoc interface for viewing the API documentation in a clean format.",
)
async def get_redoc(username: str = Depends(get_current_docs_user)):
return get_redoc_html(openapi_url="/openapi.json", title="API Documentation")
@docs_router.get(
"/openapi.json",
response_class=JSONResponse,
include_in_schema=False,
summary="OpenAPI Schema",
description="Protected OpenAPI JSON schema used for API documentation generation.",
)
async def get_openapi_json(username: str = Depends(get_current_docs_user)):
openapi_schema = get_openapi(
title="MediaTranscribe API",
version=settings.API_VERSION,
openapi_version="3.1.0",
summary="MediaTranscribe - Audio/Video Transcription API",
description="API documentation for the MediaTranscribe service.",
routes=app.routes,
terms_of_service="https://example.com/terms/",
contact={"name": "API Support", "email": "support@example.com"},
license_info={"name": "MIT License", "url": "https://opensource.org/licenses/MIT"},
tags=[{"name": "transcription", "description": "Audio and video transcription endpoints"}],
servers=[{"url": "https://api.example.com", "description": "Production server"}],
separate_input_output_schemas=True,
)
return JSONResponse(openapi_schema)
Key Features:
- Authenticated Routes: All documentation endpoints require valid credentials.
- Hidden from Schema:
include_in_schema=Falseprevents these endpoints from appearing in the public API schema. - Rich Metadata: The OpenAPI schema includes detailed metadata for better documentation.
Step 3: Disable Default Endpoints and Secure OpenAPI JSON
Disable FastAPI's default endpoints to ensure only authenticated routes are accessible, and secure the /openapi.json endpoint to address the security concern of its exposure.
from fastapi import FastAPI
from fastapi.responses import UJSONResponse
from app.core.config import settings
app = FastAPI(
title="MediaTranscribe API",
version=settings.API_VERSION,
description="API documentation for the MediaTranscribe service.",
contact={"name": "API Support", "email": "support@example.com"},
terms_of_service="https://example.com/terms/",
docs_url=None,
redoc_url=None,
openapi_url=None,
default_response_class=UJSONResponse,
)
app.include_router(docs_router, prefix="")
Why Disable Default Endpoints? Setting docs_url=None, redoc_url=None, and openapi_url=None prevents unauthorized access to default endpoints, ensuring all access goes through the authenticated routes.
Step 4: Configure Environment Settings
Use a settings module to manage environment-specific configurations, ensuring secure and flexible deployment.
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
ENV: str = "production"
DOMAIN: str = "example.com"
API_VERSION: str = "1.0.0"
API_DOCS_USERNAME: str = "admin"
API_DOCS_PASSWORD: str = "securepassword123"
OPENAPI_TAGS: list = [{"name": "transcription", "description": "Audio and video transcription endpoints"}]
OPENAPI_SERVERS: list = [{"url": "https://api.example.com", "description": "Production server"}]
CONTACT: dict = {"name": "API Support", "email": "support@example.com"}
LICENSE_INFO: dict = {"name": "MIT License", "url": "https://opensource.org/licenses/MIT"}
settings = Settings()
Best Practices:
- Environment Variables: Store sensitive data like credentials in environment variables.
- Secure Passwords: Use strong, hashed passwords in production.
- Logging: Log authentication attempts for security monitoring.
Addressing the /openapi.json Security Concern
The default /openapi.json endpoint exposes the full API schema, which can leak sensitive information. By setting openapi_url=None and creating an authenticated /openapi.json route, we ensure only authorized users can access the schema, mitigating this security risk.
Benefits of Securing API Documentation
- Enhanced Enhanced Security: Protects sensitive API details from unauthorized access.
- Controlled Access: Restricts documentation to authorized developers or partners.
- Regulatory Compliance: Aligns with data protection requirements.
- Professional Workflow: Ensures a secure, professional API development process.
Pro Tips for Production
- Use Strong Authentication: Replace HTTP Basic Authentication with OAuth2 or token-based authentication for enhanced security.
- Role-Based Access: Extend role-based controls for granular permissions.
- Rate Limiting: Apply rate limiting to documentation endpoints to prevent brute-force attacks.
- HTTPS Enforcement: Serve your API over HTTPS to encrypt credentials.
- Monitoring: Use tools like Sentry or Prometheus to monitor authentication attempts.
- Environment Configurations: Relax security in development environments while enforcing strict controls in production.
Conclusion
Securing your FastAPI documentation endpoints is essential for protecting your API in production. The basic authentication approach is quick and effective for small projects, while the advanced database-backed, role-based method provides robust security for enterprise applications. By disabling default endpoints and securing /openapi.json, you ensure comprehensive protection of your API documentation.
Next Steps:
- Implement the basic or advanced authentication method in your FastAPI project.
- Test authentication locally using the development mode.
- Explore advanced authentication like OAuth2 for production environments.
- Monitor authentication logs to ensure secure access.
Ready to secure your FastAPI documentation? Follow these steps to keep your API safe, professional, and accessible only to authorized users!

Experienced DevOps Engineer proficient in Python, Docker, Kubernetes, CI/CD, and cloud technologies. Specialized in automating processes and building scalable infrastructure for seamless development and deployment.


