Introduction to Security Schemes
The security system in this codebase is built upon a hierarchy of classes that integrate directly with FastAPI's dependency injection and OpenAPI generation systems. At the root of this hierarchy is the SecurityBase class.
The Role of SecurityBase
The SecurityBase class, defined in fastapi/security/base.py, serves as the fundamental building block for all authentication and authorization schemes. It is a simple class with two primary attributes:
class SecurityBase:
model: SecurityBaseModel
scheme_name: str
In this codebase, SecurityBase performs two critical roles:
- Dependency Identification: It acts as a marker for the dependency injection system. When a dependency is added to a route, FastAPI checks if it is an instance of
SecurityBaseto determine if it should be treated as a security requirement. - Metadata Container: It holds the
model(an OpenAPI security scheme model) and thescheme_nameused to generate thecomponents/securitySchemessection of the OpenAPI documentation.
Integration with Dependency Injection
The dependency injection system uses SecurityBase to distinguish between regular dependencies and security schemes. This logic is implemented in fastapi/dependencies/models.py within the Dependant class.
The Dependant class uses a cached property _is_security_scheme to identify these objects:
@cached_property
def _is_security_scheme(self) -> bool:
if self.call is None:
return False
unwrapped = _unwrapped_call(self.call)
return isinstance(unwrapped, SecurityBase)
When isinstance(unwrapped, SecurityBase) returns True, FastAPI knows that this dependency requires special handling, such as extracting security scopes or populating the security requirements in the generated OpenAPI schema.
Implementing Security Schemes
While SecurityBase provides the interface, it does not implement the logic for extracting credentials. Specific security implementations (like API Keys, HTTP Basic Auth, or OAuth2) subclass SecurityBase and implement a __call__ method. This allows the class instances to be used directly as dependencies.
API Key Example
The APIKeyQuery class (found in fastapi/security/api_key.py) inherits from APIKeyBase, which in turn inherits from SecurityBase. It implements the logic to look for a specific query parameter:
from fastapi import Depends, FastAPI
from fastapi.security import APIKeyQuery
app = FastAPI()
# query_scheme is an instance of a class inheriting from SecurityBase
query_scheme = APIKeyQuery(name="api_key")
@app.get("/items/")
async def read_items(api_key: str = Depends(query_scheme)):
return {"api_key": api_key}
HTTP Authentication Example
Similarly, HTTPBasic (found in fastapi/security/http.py) inherits from HTTPBase, which inherits from SecurityBase. It handles the Authorization header and returns an HTTPBasicCredentials object:
from typing import Annotated
from fastapi import Depends, FastAPI
from fastapi.security import HTTPBasic, HTTPBasicCredentials
app = FastAPI()
security = HTTPBasic()
@app.get("/users/me")
def read_current_user(credentials: Annotated[HTTPBasicCredentials, Depends(security)]):
return {"username": credentials.username, "password": credentials.password}
The auto_error Pattern
A common pattern across all security schemes in this codebase is the auto_error parameter. This boolean flag determines the behavior when authentication fails (e.g., a missing header or invalid token).
- If
auto_error=True(the default), the scheme will automatically raise anHTTPException(typically with a 401 or 403 status code) if credentials are missing or invalid. - If
auto_error=False, the scheme will returnNoneinstead of raising an error, allowing the path operation function to handle the missing authentication manually.
This is implemented in the __call__ method of the subclasses. For example, in HTTPBase, the code checks the authorization header and, if missing, either raises an error or returns None based on this setting.
OpenAPI Schema Generation
The model attribute in SecurityBase is used by the OpenAPI utility functions in fastapi/openapi/utils.py. When FastAPI generates the JSON schema for your API, it iterates through the dependencies. If it finds a Dependant where _is_security_scheme is true, it accesses the _security_scheme.model to populate the security definitions.
This ensures that any class inheriting from SecurityBase automatically documents itself in the Swagger UI, showing the correct authentication requirements (e.g., a lock icon next to the route) based on the specific subclass used.