From 2702e5bcd6a1c7bf947eeb87e122b48995947a22 Mon Sep 17 00:00:00 2001 From: Avoca9o Date: Mon, 3 Jun 2024 23:02:26 +0300 Subject: [PATCH] [feature] add oauth 2 client credential classx --- fastapi/security/__init__.py | 1 + fastapi/security/oauth2.py | 94 ++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) diff --git a/fastapi/security/__init__.py b/fastapi/security/__init__.py index 3aa6bf21e44f3..e951f21fc2980 100644 --- a/fastapi/security/__init__.py +++ b/fastapi/security/__init__.py @@ -8,6 +8,7 @@ from .http import HTTPDigest as HTTPDigest from .oauth2 import OAuth2 as OAuth2 from .oauth2 import OAuth2AuthorizationCodeBearer as OAuth2AuthorizationCodeBearer +from .oauth2 import OAuth2ClientCredentials as OAuth2ClientCredentials from .oauth2 import OAuth2PasswordBearer as OAuth2PasswordBearer from .oauth2 import OAuth2PasswordRequestForm as OAuth2PasswordRequestForm from .oauth2 import OAuth2PasswordRequestFormStrict as OAuth2PasswordRequestFormStrict diff --git a/fastapi/security/oauth2.py b/fastapi/security/oauth2.py index 9720cace0575e..89dffe7cbc93d 100644 --- a/fastapi/security/oauth2.py +++ b/fastapi/security/oauth2.py @@ -595,6 +595,100 @@ async def __call__(self, request: Request) -> Optional[str]: return param +class OAuth2ClientCredentials(OAuth2): + """ + OAuth2 flow for authentication using a bearer token obtained with an OAuth2 client + credentials flow. An instance of it would be used as a dependency. + """ + + def __init__( + self, + tokenUrl: Annotated[ + str, + Doc( + """ + The URL to obtain the OAuth2 token. + """ + ), + ], + scheme_name: Annotated[ + Optional[str], + Doc( + """ + Security scheme name. + It will be included in the generated OpenAPI (e.g. visible at `/docs`). + """ + ), + ] = None, + scopes: Annotated[ + Optional[Dict[str, str]], + Doc( + """ + The OAuth2 scopes that would be required by the *"path" operations* that + use this dependency. + """ + ), + ] = None, + description: Annotated[ + Optional[str], + Doc( + """ + Security scheme description. + It will be included in the generated OpenAPI (e.g. visible at `/docs`). + """ + ), + ] = None, + auto_error: Annotated[ + bool, + Doc( + """ + By default, if no HTTP Auhtorization header is provided, required for + OAuth2 authentication, it will automatically cancel the request and + send the client an error. + If `auto_error` is set to `False`, when the HTTP Authorization header + is not available, instead of erroring out, the dependency result will + be `None`. + This is useful when you want to have optional authentication. + It is also useful when you want to have authentication that can be + provided in one of multiple optional ways (for example, with OAuth2 + or in a cookie). + """ + ), + ] = True, + ): + if not scopes: + scopes = {} + flows = OAuthFlowsModel( + clientCredentials=cast( + Any, + { + "tokenUrl": tokenUrl, + "scopes": scopes, + }, + ) + ) + super().__init__( + flows=flows, + scheme_name=scheme_name, + description=description, + auto_error=auto_error, + ) + + async def __call__(self, request: Request) -> Optional[str]: + authorization = request.headers.get("Authorization") + scheme, param = get_authorization_scheme_param(authorization) + if not authorization or scheme.lower() != "bearer": + if self.auto_error: + raise HTTPException( + status_code=HTTP_401_UNAUTHORIZED, + detail="Not authenticated", + headers={"WWW-Authenticate": "Bearer"}, + ) + else: + return None # pragma: nocover + return param + + class SecurityScopes: """ This is a special class that you can define in a parameter in a dependency to