Añadir contraseña a Swagger en FastAPI

Tiempo de lectura: 2 minutos

Hoy vamos a aprender cómo podemos añadir una contraseña de acceso a Swagger en FastAPI. De esta forma securizaremos en dashboard de Swagger y el json de definiciones.

Cascada impresionante - Pexels

Lo primero que haremos es ir al main.py de FastAPI.

Vamos a crear un Middleware que nos permitirá interceptar las llamadas al endpoint /openapi.json y /docs y solicitaremos un usuario y contraseña en esos endpoints.

import os
from dotenv import load_dotenv
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from starlette.middleware.base import BaseHTTPMiddleware
import base64

load_dotenv()


# Configuración de la autenticación básica
security = HTTPBasic()
valid_username = os.getenv("SWAGGER_USER")
valid_password = os.getenv("SWAGGER_PASSWORD")

def authenticate(credentials: HTTPBasicCredentials = Depends()):
    if credentials.username != valid_username or credentials.password != valid_password:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Incorrect username or password",
            headers={"WWW-Authenticate": "Basic"},
        )


# Middleware para autenticar las rutas de documentación
class AuthDocsMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        # Verificamos si es una solicitud a /docs o /openapi.json (y cualquier otra ruta que contenga "openapi.json")
        if "/openapi.json" in request.url.path or "/docs" in request.url.path:
            auth_header = request.headers.get("Authorization")
            if not auth_header or not auth_header.startswith("Basic "):
                # Si no hay encabezado Authorization, respondemos con 401 y el encabezado WWW-Authenticate
                return JSONResponse(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    content={"detail": "Missing or invalid authorization header"},
                    headers={"WWW-Authenticate": "Basic"},
                )

            # Extraemos y decodificamos las credenciales
            try:
                base64_credentials = auth_header[6:]  # Eliminamos el prefijo "Basic "
                decoded_credentials = base64.b64decode(base64_credentials).decode("utf-8")
                username, password = decoded_credentials.split(":", 1)

                # Creamos el objeto de credenciales
                credentials = HTTPBasicCredentials(username=username, password=password)

                # Ejecutamos la función de autenticación
                authenticate(credentials)

            except Exception as e:
                return JSONResponse(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    content={"detail": "Incorrect username or password"},
                    headers={"WWW-Authenticate": "Basic"},
                )

        return await call_next(request)


# Agregamos el middleware
app.add_middleware(AuthDocsMiddleware)

Ahora crearemos un archivo .env con los credenciales necesarios:

.env

SWAGGER_USER="admin"
SWAGGER_PASS="admin123"

Y cuándo accedamos al Swagger /docs nos pedirá autenticación:

Solicitando acceso por contraseña al Swagger

Deja un comentario