Customizing Authentication Errors
To customize authentication error responses in this codebase, you can override the make_not_authenticated_error method in any security class derived from APIKeyBase, HTTPBase, OAuth2, or OpenIdConnect.
Overriding the Error Status Code
By default, security schemes in this project return a 401 Unauthorized status code when authentication fails. You can change this to 403 Forbidden or any other status code by subclassing the security scheme and providing a custom implementation of make_not_authenticated_error.
from typing import Annotated
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
app = FastAPI()
class HTTPBearer403(HTTPBearer):
def make_not_authenticated_error(self) -> HTTPException:
return HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Custom authentication failure message"
)
# Use the customized scheme as a dependency
security = HTTPBearer403()
@app.get("/secure-data")
def get_secure_data(credentials: Annotated[HTTPAuthorizationCredentials, Depends(security)]):
return {"message": "Access granted", "token": credentials.credentials}
Customizing WWW-Authenticate Headers
The HTTP specification (RFC 9110) requires that a 401 Unauthorized response include a WWW-Authenticate header. This codebase provides default challenges for different schemes:
- APIKeyBase:
WWW-Authenticate: APIKey - HTTPBase: Uses the scheme name (e.g.,
BasicorBearer) - OAuth2 / OpenIdConnect:
WWW-Authenticate: Bearer
Customizing HTTP Challenges and Realms
For HTTPBase subclasses like HTTPBasic, you can override make_authenticate_headers to include additional parameters like a realm.
from fastapi.security import HTTPBasic
class CustomHTTPBasic(HTTPBasic):
def make_authenticate_headers(self) -> dict[str, str]:
# Customizing the realm or adding other challenge parameters
return {"WWW-Authenticate": 'Basic realm="restricted-area", charset="UTF-8"'}
security = CustomHTTPBasic()
Customizing API Key Challenges
Since API Key authentication is not standardized in the HTTP spec, APIKeyBase uses a custom APIKey challenge. You can override this to use a different identifier or provide more context.
from fastapi.security import APIKeyHeader
class CustomAPIKeyHeader(APIKeyHeader):
def make_not_authenticated_error(self) -> HTTPException:
return HTTPException(
status_code=401,
detail="API Key required",
headers={"WWW-Authenticate": "APIKey realm='api'"}
)
security = CustomAPIKeyHeader(name="X-API-Key")
Handling Errors Manually with auto_error=False
If you want to avoid raising an exception automatically and instead handle the missing credentials within your path operation logic, set auto_error=False. When this is set, the dependency returns None instead of raising an HTTPException.
from fastapi.security import OAuth2PasswordBearer
# Initialize with auto_error=False
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token", auto_error=False)
@app.get("/optional-auth")
def read_items(token: Annotated[str | None, Depends(oauth2_scheme)]):
if token is None:
# Perform custom logic for unauthenticated users
return {"message": "Hello anonymous user"}
return {"message": f"Hello authenticated user with token {token}"}
Troubleshooting
- Missing WWW-Authenticate Header: If you override
make_not_authenticated_errorand return a401status code, ensure you include theWWW-Authenticateheader in theHTTPException. Failing to do so violates RFC 9110. - Scheme Mismatch: When subclassing
HTTPBase, ensure yourmake_authenticate_headersmatches theschemedefined in the constructor, as this is used for OpenAPI documentation generation. - APIKey Standardization: Note that
APIKeyBaseuses a non-standardAPIKeychallenge by default to satisfy the requirement for aWWW-Authenticateheader on 401 responses.