Skip to main content

OpenID Connect Integration

In FastAPI, the OpenID Connect (OIDC) implementation is designed with a "metadata-first" philosophy. Rather than providing a heavy, opinionated client that handles OIDC discovery and token validation out of the box, the codebase provides a lightweight stub. This approach allows developers to define their security requirements for OpenAPI documentation while maintaining full control over the actual authentication logic.

The Role of the OpenIdConnect Stub

The OpenIdConnect class, located in fastapi/security/open_id_connect_url.py, serves primarily as a bridge between your application code and the generated OpenAPI specification. When you instantiate this class, you are essentially telling FastAPI (and subsequently Swagger UI) that your API is protected by an OIDC provider located at a specific URL.

The implementation is intentionally minimal to avoid forcing specific dependencies (like JWT libraries or HTTP clients for discovery) on the developer. As noted in the class docstring:

Warning: this is only a stub to connect the components with OpenAPI in FastAPI, but it doesn't implement the full OpenIdConnect scheme... You would need to subclass it and implement it in your code.

Implementation Details

The OpenIdConnect class inherits from SecurityBase and focuses on two main tasks: defining the security model and extracting the authorization header.

Configuration and Metadata

During initialization, the class takes an openIdConnectUrl, which is used to populate the OpenIdConnectModel. This model is what eventually appears in the /docs endpoint.

def __init__(
self,
*,
openIdConnectUrl: str,
scheme_name: str | None = None,
description: str | None = None,
auto_error: bool = True,
):
self.model = OpenIdConnectModel(
openIdConnectUrl=openIdConnectUrl, description=description
)
self.scheme_name = scheme_name or self.__class__.__name__
self.auto_error = auto_error

Request Handling

The __call__ method is the entry point when the class is used as a FastAPI dependency. Its logic is straightforward: it checks for the presence of an Authorization header.

async def __call__(self, request: Request) -> str | None:
authorization = request.headers.get("Authorization")
if not authorization:
if self.auto_error:
raise self.make_not_authenticated_error()
else:
return None
return authorization

Unlike other security classes in this codebase (such as OAuth2PasswordBearer), OpenIdConnect does not attempt to parse the header or strip the "Bearer " prefix. It returns the raw string, leaving the parsing and validation to the developer or a subclass.

Design Tradeoffs and Constraints

The design of OpenIdConnect reflects several key tradeoffs:

  1. Flexibility vs. Convenience: By not implementing OIDC discovery (fetching the .well-known/openid-configuration), FastAPI avoids a mandatory dependency on an asynchronous HTTP client. This keeps the core framework lean but requires the developer to handle the network calls necessary for OIDC.
  2. Schema Accuracy: The primary value of this class is ensuring that the securitySchemes section of the OpenAPI JSON is correctly formatted. This allows tools like Swagger UI to show the "Authorize" button and prompt for the correct OIDC flow.
  3. Error Handling: Through the auto_error parameter, the implementation supports both mandatory and optional authentication. If auto_error is False, the dependency returns None instead of raising an HTTPException, allowing for complex logic where multiple authentication methods might be tried in sequence.

Extending OpenIdConnect for Production

To use OpenID Connect in a production environment, you are expected to subclass OpenIdConnect and override the __call__ method. A typical implementation would:

  1. Call super().__call__(request) to get the raw header.
  2. Parse the "Bearer " token.
  3. Validate the token against the provider's public keys (often found via the openIdConnectUrl).

In its base form, as seen in tests/test_security_openid_connect.py, the class is used to simply pass the header through to a downstream dependency:

from fastapi import Depends, FastAPI, Security
from fastapi.security.open_id_connect_url import OpenIdConnect

app = FastAPI()

oid = OpenIdConnect(openIdConnectUrl="/openid")

def get_current_user(oauth_header: str = Security(oid)):
# In a real app, you would validate the token here
return {"token": oauth_header}

@app.get("/users/me")
def read_current_user(current_user: dict = Depends(get_current_user)):
return current_user

This pattern demonstrates how the stub fulfills its role: it satisfies the FastAPI dependency injection system and populates the OpenAPI metadata, while remaining agnostic about the specific validation logic required by the OIDC provider.