Skip to main content

Introduction to Security Dependencies

When you want to secure your FastAPI endpoints, you need a way to both validate incoming credentials and ensure those requirements appear correctly in the generated OpenAPI documentation (Swagger UI). Without a structured security system, you would have to manually parse headers or query parameters and manually update your OpenAPI schema.

FastAPI solves this using a hierarchy of security classes rooted in SecurityBase.

The Role of SecurityBase

The SecurityBase class in fastapi/security/base.py is the foundational building block for all authentication schemes in FastAPI. It serves two critical roles:

  1. OpenAPI Metadata: It stores the model (an OpenAPI security model) and scheme_name used to populate the components/securitySchemes section of your API's JSON schema.
  2. Dependency Marker: It acts as a signal to FastAPI's dependency injection system. When you use a class inheriting from SecurityBase with Depends or Security, FastAPI recognizes it as a security requirement.
class SecurityBase:
model: SecurityBaseModel
scheme_name: str

Using Security Dependencies

To secure an endpoint, you instantiate a security scheme (like APIKeyQuery, HTTPBearer, or OAuth2PasswordBearer) and pass it to a dependency.

In this example, using APIKeyQuery (which inherits from SecurityBase via APIKeyBase) ensures that the "Authorize" button appears in Swagger UI and that the key query parameter is automatically extracted.

from fastapi import Depends, FastAPI, Security
from fastapi.security import APIKeyQuery

app = FastAPI()

# Instantiate the scheme
api_key_scheme = APIKeyQuery(name="key")

@app.get("/users/me")
def read_current_user(api_key: str = Security(api_key_scheme)):
return {"api_key": api_key}

Security vs. Depends

While you can use Depends(api_key_scheme), using Security(api_key_scheme) allows you to define scopes. For many simple schemes, they behave identically, but Security is the preferred way to signal that a dependency is specifically for authentication and authorization.

How Security Schemes Work Internally

Every security scheme in FastAPI follows a specific implementation pattern found in fastapi/security/api_key.py, fastapi/security/http.py, and fastapi/security/oauth2.py.

1. OpenAPI Integration

When you initialize a scheme like APIKeyQuery, it populates the self.model attribute with an APIKey model. FastAPI's OpenAPI generator iterates through all dependencies; if it finds an instance of SecurityBase, it extracts this model to build the security definitions.

2. Request Handling via __call__

For a security scheme to work as a dependency, it must implement the __call__ method. This method receives the starlette.requests.Request and returns the credentials.

For example, APIKeyQuery.__call__ looks like this:

async def __call__(self, request: Request) -> str | None:
api_key = request.query_params.get(self.model.name)
return self.check_api_key(api_key)

3. The auto_error Pattern

Most built-in schemes include an auto_error parameter (defaulting to True). This logic is typically handled in a base class method like check_api_key in APIKeyBase:

def check_api_key(self, api_key: str | None) -> str | None:
if not api_key:
if self.auto_error:
raise HTTPException(
status_code=HTTP_401_UNAUTHORIZED,
detail="Not authenticated",
headers={"WWW-Authenticate": "APIKey"},
)
return None
return api_key

If auto_error is False, the dependency returns None instead of raising a 401 Unauthorized error. This is useful for endpoints where authentication is optional.

Creating Custom Security Schemes

If the built-in schemes don't fit your needs, you can create a custom one by inheriting from SecurityBase. You must provide a model for OpenAPI and implement __call__ for the logic.

from fastapi.security.base import SecurityBase
from fastapi.openapi.models import APIKey, APIKeyIn
from starlette.requests import Request
from starlette.exceptions import HTTPException

class CustomHeaderAuth(SecurityBase):
def __init__(self, *, name: str, auto_error: bool = True):
self.model = APIKey(**{"in": APIKeyIn.header}, name=name)
self.scheme_name = self.__class__.__name__
self.auto_error = auto_error

async def __call__(self, request: Request) -> str | None:
header_value = request.headers.get(self.model.name)
if not header_value and self.auto_error:
raise HTTPException(status_code=403, detail="Missing Custom Header")
return header_value

By inheriting from SecurityBase, your custom class is automatically recognized by FastAPI's OpenAPI generator, ensuring your custom security logic is documented without extra configuration.