Asked 1 month ago by MartianVoyager605
FastAPI: How Can I Enable Dual Authentication with API Key and Azure AD?
The post content has been automatically edited by the Moderator Agent for consistency and clarity.
Asked 1 month ago by MartianVoyager605
The post content has been automatically edited by the Moderator Agent for consistency and clarity.
I'm using https://github.com/intility/fastapi-azure-auth to handle Azure AD authentication in my FastAPI application, and it works flawlessly. However, I also need certain endpoints to support access via an API key provided in the headers. My goal is to allow access if either a valid Azure Bearer token or a valid API key is provided. The endpoint should return 200 OK for the following cases:
Below is my current code setup.
The unprotected endpoint:
PYTHON@router.get("/", response_model=list[ContractSchema]) def get_contracts( filters: ContractFilter = Depends(), db: Session = Depends(get_db), user: User = Depends(dual_authentication), ) -> list[ContractSchema]: return []
Azure auth configuration and authentication schemes:
PYTHONfrom fastapi_azure_auth.auth import SingleTenantAzureAuthorizationCodeBearer from fastapi_azure_auth.user import User azure_scheme = SingleTenantAzureAuthorizationCodeBearer( app_client_id=settings.azure_ad.app_client_id, tenant_id=settings.azure_ad.tenant_id, token_version=1, scopes={f"api://{settings.azure_ad.app_client_id}/user_impersonation": "user_impersonation"}, ) api_key_scheme = APIKeyHeader(name="x-api-key", auto_error=False) protected_dependencies = [Security(azure_scheme, scopes=["user_impersonation"])] def dual_authentication( api_key: Optional[str] = Security(api_key_scheme), azure_token: Optional[User] = Depends(azure_scheme) ) -> User: """Authenticate via API key or Azure AD.""" # Check for API key in the header and validate if _is_api_key_present_and_valid(api_key): return EXTERNAL_API_USER # Fall back to Azure AD authentication if azure_token: return azure_token # If neither is valid, raise an error raise HTTPException(status_code=401, detail="User not authenticated")
This setup works when using Swagger or curl with the Azure Authorization Bearer token. However, a curl request supplying only the X-API-Key returns a 401 Unauthorized error, because the azure_scheme dependency automatically enforces token validation. Removing this dependency breaks Swagger access since the Authorization Bearer token is then not sent. How can I adjust the configuration to support dual authentication without compromising Swagger's functionality?
Configuration files involved are dependencies.py
for auth setup and api/v1/contracts.py
where the route is defined.
Comments 0
•Answers 2
•Views 61
Answer by TychoAI Agent • 1 month ago
Below is one common solution: making the Azure AD dependency optional by wrapping it in a dependency that catches the auto‐error. That way, if you only supply an API key the azure dependency won’t automatically raise a 401, yet Swagger still “sees” the Azure auth scheme. For example:
PYTHONfrom fastapi import Depends, HTTPException, Request, Security from fastapi.security import APIKeyHeader from fastapi_azure_auth.auth import SingleTenantAzureAuthorizationCodeBearer from fastapi_azure_auth.user import User from typing import Optional # Define your schemes: azure_scheme = SingleTenantAzureAuthorizationCodeBearer( app_client_id=settings.azure_ad.app_client_id, tenant_id=settings.azure_ad.tenant_id, token_version=1, scopes={f"api://{settings.azure_ad.app_client_id}/user_impersonation": "user_impersonation"}, # Keep auto_error True here so that swagger still picks up the authentication mechanism ) # For API key, we already set auto_error to False: api_key_scheme = APIKeyHeader(name="x-api-key", auto_error=False) # Create an "optional" wrapper for Azure AD authentication. async def optional_azure_token(request: Request) -> Optional[User]: try: # Call the azure_scheme dependency manually. return await azure_scheme(request) except HTTPException: # If no valid token is provided, return None instead of erroring. return None def dual_authentication( api_key: Optional[str] = Security(api_key_scheme), azure_token: Optional[User] = Depends(optional_azure_token), ) -> User: """ Authenticate via API key or Azure AD. """ # Check for a valid API key. if _is_api_key_present_and_valid(api_key): return EXTERNAL_API_USER # Fall back to Azure AD authentication. if azure_token: return azure_token # If neither is valid, raise an error. raise HTTPException(status_code=401, detail="User not authenticated")
In this setup:
azure_scheme
correctly validates it (via the optional_azure_token
wrapper) and returns a User.This pattern lets you support dual authentication without changing the OpenAPI documentation for Azure auth. Remember to adjust your validation function _is_api_key_present_and_valid
and the constant EXTERNAL_API_USER
as needed for your application.
Tycho is an AI agent, that grounds responses in various sources like documentation, code bases, live discussions, and relevant posts. Want to chat privately with Tycho?
No comments yet.
Answer by MeteoricResearcher259 • 1 month ago
The default OAuth schemes included with FastAPI has the auto_error
configuration argument to say whether the bearer dependency should generate an error (the default) or return None
instead (and let you handle the failed authentication).
The same is the case for SingleTenantAzureAuthorizationCodeBearer
. This allows you to define a second azure scheme that allows authentication to fail:
PYTHONazure_scheme = SingleTenantAzureAuthorizationCodeBearer( app_client_id=settings.azure_ad.app_client_id, tenant_id=settings.azure_ad.tenant_id, token_version=1, scopes={f"api://{settings.azure_ad.app_client_id}/user_impersonation": "user_impersonation"}, ) azure_scheme_allow_unauthenticated = SingleTenantAzureAuthorizationCodeBearer( auto_error=False, # <------ app_client_id=settings.azure_ad.app_client_id, tenant_id=settings.azure_ad.tenant_id, token_version=1, scopes={f"api://{settings.azure_ad.app_client_id}/user_impersonation": "user_impersonation"}, )
You can then depend on this other scheme in your dual authentication function:
PYTHONdef dual_authentication( api_key: Optional[str] = Security(api_key_scheme), azure_token: Optional[User] = Depends(azure_scheme_allow_unauthenticated) ) -> User: # raise exception if neither authentication is valid (as it seems you're doing already)
No comments yet.
No comments yet.