Skip to main content

OpenID Connect Integration

When you want your API to support OpenID Connect (OIDC) so that tools like Swagger UI can display the "Authorize" button and redirect users to an identity provider, you need a way to declare that security scheme in your OpenAPI specification. FastAPI provides the OpenIdConnect class specifically for this purpose, acting as a bridge between your application logic and the generated documentation.

Documentation-First Design

The OpenIdConnect class in fastapi/security/open_id_connect_url.py is implemented as a "stub." This design choice reflects the complexity of the OpenID Connect standard; rather than providing a heavy, opinionated OIDC client that might not fit every provider (like Auth0, Keycloak, or Google), FastAPI provides the necessary hooks to satisfy the OpenAPI 3.1.0 specification while leaving the specific validation logic to the developer.

When you instantiate OpenIdConnect, it populates the securitySchemes section of your OpenAPI schema using the OpenIdConnectModel defined in fastapi/openapi/models.py.

from fastapi import FastAPI
from fastapi.security import OpenIdConnect

app = FastAPI()

# Define the OIDC scheme for OpenAPI documentation
oidc_scheme = OpenIdConnect(
openIdConnectUrl="https://example.com/.well-known/openid-configuration",
description="OpenID Connect authentication"
)

Dependency Behavior and Header Extraction

As a security dependency, OpenIdConnect implements a __call__ method that allows it to be used with FastAPI's Depends or Security. Its primary runtime behavior is to extract the Authorization header from the incoming request.

The implementation in fastapi/security/open_id_connect_url.py is straightforward:

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

If the header is present, the dependency returns the raw string value. It does not verify if the string starts with "Bearer " or perform any JWT decoding. This ensures that the dependency remains lightweight and compatible with various token formats.

Optional Authentication with auto_error

The auto_error parameter allows you to define whether authentication is strictly required for an endpoint. By default, auto_error is True, and FastAPI will raise an HTTPException with a 401 status code if the Authorization header is missing.

Setting auto_error=False is useful for endpoints where you want to provide enhanced functionality for authenticated users but still allow guest access.

from typing import Annotated
from fastapi import Security

# Optional OIDC dependency
oidc_optional = OpenIdConnect(
openIdConnectUrl="/openid",
auto_error=False
)

@app.get("/items")
def read_items(auth_header: Annotated[str | None, Security(oidc_optional)]):
if auth_header:
return {"mode": "authenticated", "header": auth_header}
return {"mode": "guest"}

Implementing Real Validation

Because OpenIdConnect is a stub, it does not fetch the configuration from the openIdConnectUrl or validate tokens against a public key. In a production environment, you are expected to use the string returned by the dependency to perform your own validation, typically by subclassing OpenIdConnect or using it as a sub-dependency.

A common pattern found in FastAPI tests (e.g., tests/test_security_openid_connect.py) involves wrapping the OIDC dependency in another function that handles the business logic of user resolution:

from fastapi import Depends, HTTPException, Security
from fastapi.security import OpenIdConnect
from pydantic import BaseModel

oid = OpenIdConnect(openIdConnectUrl="/openid")

class User(BaseModel):
username: str

def get_current_user(oauth_header: Annotated[str, Security(oid)]):
# In a real application, you would:
# 1. Parse the 'Bearer ' prefix
# 2. Decode the JWT
# 3. Validate the signature and expiration
# 4. Look up the user in a database
if not oauth_header.startswith("Bearer "):
raise HTTPException(status_code=401, detail="Invalid token type")

return User(username="johndoe")

@app.get("/users/me")
def read_current_user(current_user: Annotated[User, Depends(get_current_user)]):
return current_user

This separation of concerns allows FastAPI to handle the OpenAPI metadata and header extraction while giving you full control over the security implementation details.