Skip to main content

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., Basic or Bearer)
  • 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_error and return a 401 status code, ensure you include the WWW-Authenticate header in the HTTPException. Failing to do so violates RFC 9110.
  • Scheme Mismatch: When subclassing HTTPBase, ensure your make_authenticate_headers matches the scheme defined in the constructor, as this is used for OpenAPI documentation generation.
  • APIKey Standardization: Note that APIKeyBase uses a non-standard APIKey challenge by default to satisfy the requirement for a WWW-Authenticate header on 401 responses.