@ -7,53 +7,21 @@ from starlette.exceptions import HTTPException
from starlette . requests import Request
from starlette . requests import Request
from starlette . status import HTTP_401_UNAUTHORIZED
from starlette . status import HTTP_401_UNAUTHORIZED
DOCS = {
class OpenIdConnect ( SecurityBase ) :
" url " : Doc ( """
"""
OpenID Connect authentication class . An instance of it would be used as a
dependency .
* * Warning * * : this is only a stub to connect the components with OpenAPI in FastAPI ,
but it doesn ' t implement the full OpenIdConnect scheme, for example, it doesn ' t use
the OpenIDConnect URL . You would need to subclass it and implement it in your
code .
"""
def __init__ (
self ,
* ,
openIdConnectUrl : Annotated [
str ,
Doc (
"""
The OpenID Connect URL .
The OpenID Connect URL .
"""
""" ),
) ,
" scheme " : Doc ( """
] ,
scheme_name : Annotated [
str | None ,
Doc (
"""
Security scheme name .
Security scheme name .
It will be included in the generated OpenAPI ( e . g . visible at ` / docs ` ) .
It will be included in the generated OpenAPI ( e . g . visible at ` / docs ` ) .
"""
""" ),
) ,
" desc " : Doc ( """
] = None ,
description : Annotated [
str | None ,
Doc (
"""
Security scheme description .
Security scheme description .
It will be included in the generated OpenAPI ( e . g . visible at ` / docs ` ) .
It will be included in the generated OpenAPI ( e . g . visible at ` / docs ` ) .
"""
""" ),
) ,
" auto " : Doc ( """
] = None ,
auto_error : Annotated [
bool ,
Doc (
"""
By default , if no HTTP Authorization header is provided , required for
By default , if no HTTP Authorization header is provided , required for
OpenID Connect authentication , it will automatically cancel the request
OpenID Connect authentication , it will automatically cancel the request
and send the client an error .
and send the client an error .
@ -67,28 +35,63 @@ class OpenIdConnect(SecurityBase):
It is also useful when you want to have authentication that can be
It is also useful when you want to have authentication that can be
provided in one of multiple optional ways ( for example , with OpenID
provided in one of multiple optional ways ( for example , with OpenID
Connect or in a cookie ) .
Connect or in a cookie ) .
"""
""" )
) ,
}
] = True ,
class OpenIdConnect ( SecurityBase ) :
"""
OpenID Connect authentication class . An instance of it would be used as a
dependency .
* * Warning * * : this is only a stub to connect the components with OpenAPI in FastAPI ,
but it doesn ' t implement the full OpenIdConnect scheme, for example, it doesn ' t use
the OpenIDConnect URL . You would need to subclass it and implement it in your
code .
"""
def __init__ (
self ,
* ,
openIdConnectUrl : Annotated [ str , DOCS [ " url " ] ] ,
scheme_name : Annotated [ str | None , DOCS [ " scheme " ] ] = None ,
description : Annotated [ str | None , DOCS [ " desc " ] ] = None ,
auto_error : Annotated [ bool , DOCS [ " auto " ] ] = True ,
) :
) :
self . model = OpenIdConnectModel (
self . model = OpenIdConnectModel ( openIdConnectUrl = openIdConnectUrl , description = description )
openIdConnectUrl = openIdConnectUrl , description = description
)
self . scheme_name = scheme_name or self . __class__ . __name__
self . scheme_name = scheme_name or self . __class__ . __name__
self . auto_error = auto_error
self . auto_error = auto_error
def make_not_authenticated_error ( self ) - > HTTPException :
def make_not_authenticated_error ( self , detail : str = " Not authenticated " ) - > HTTPException :
return HTTPException (
return HTTPException (
status_code = HTTP_401_UNAUTHORIZED ,
status_code = HTTP_401_UNAUTHORIZED ,
detail = " Not authenticated " ,
detail = detail ,
headers = { " WWW-Authenticate " : " Bearer " } ,
headers = { " WWW-Authenticate " : " Bearer " } ,
)
)
async def __call__ ( self , request : Request ) - > str | None :
async def __call__ ( self , request : Request ) - > str | None :
authorization = request . headers . get ( " Authorization " )
authorization = request . headers . get ( " Authorization " )
# Case 1: Header is entirely missing
if not authorization :
if not authorization :
if self . auto_error :
if self . auto_error :
raise self . make_not_authenticated_error ( )
raise self . make_not_authenticated_error ( " Missing ' Authorization ' header in request. " )
else :
return None
return None
# Case 2: Header exists but uses the wrong protocol/scheme (e.g., Basic, APIKey, or raw token)
if not authorization . lower ( ) . startswith ( " bearer " ) :
if self . auto_error :
raise self . make_not_authenticated_error (
" Invalid authentication credentials. Expected a ' Bearer <token> ' prefix. "
)
return None
# Case 3: 'Bearer ' prefix is there, but no actual token follows it
token = authorization [ 7 : ] . strip ( )
if not token :
if self . auto_error :
raise self . make_not_authenticated_error (
" Authentication token value is empty or missing after ' Bearer ' prefix. "
)
return None
return authorization
return authorization