about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/fastapi/security/oauth2.py
diff options
context:
space:
mode:
authorS. Solomon Darnell2025-03-28 21:52:21 -0500
committerS. Solomon Darnell2025-03-28 21:52:21 -0500
commit4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch)
treeee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/fastapi/security/oauth2.py
parentcc961e04ba734dd72309fb548a2f97d67d578813 (diff)
downloadgn-ai-master.tar.gz
two version of R2R are here HEAD master
Diffstat (limited to '.venv/lib/python3.12/site-packages/fastapi/security/oauth2.py')
-rw-r--r--.venv/lib/python3.12/site-packages/fastapi/security/oauth2.py638
1 files changed, 638 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/fastapi/security/oauth2.py b/.venv/lib/python3.12/site-packages/fastapi/security/oauth2.py
new file mode 100644
index 00000000..5ffad598
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/fastapi/security/oauth2.py
@@ -0,0 +1,638 @@
+from typing import Any, Dict, List, Optional, Union, cast
+
+from fastapi.exceptions import HTTPException
+from fastapi.openapi.models import OAuth2 as OAuth2Model
+from fastapi.openapi.models import OAuthFlows as OAuthFlowsModel
+from fastapi.param_functions import Form
+from fastapi.security.base import SecurityBase
+from fastapi.security.utils import get_authorization_scheme_param
+from starlette.requests import Request
+from starlette.status import HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN
+
+# TODO: import from typing when deprecating Python 3.9
+from typing_extensions import Annotated, Doc
+
+
+class OAuth2PasswordRequestForm:
+    """
+    This is a dependency class to collect the `username` and `password` as form data
+    for an OAuth2 password flow.
+
+    The OAuth2 specification dictates that for a password flow the data should be
+    collected using form data (instead of JSON) and that it should have the specific
+    fields `username` and `password`.
+
+    All the initialization parameters are extracted from the request.
+
+    Read more about it in the
+    [FastAPI docs for Simple OAuth2 with Password and Bearer](https://fastapi.tiangolo.com/tutorial/security/simple-oauth2/).
+
+    ## Example
+
+    ```python
+    from typing import Annotated
+
+    from fastapi import Depends, FastAPI
+    from fastapi.security import OAuth2PasswordRequestForm
+
+    app = FastAPI()
+
+
+    @app.post("/login")
+    def login(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]):
+        data = {}
+        data["scopes"] = []
+        for scope in form_data.scopes:
+            data["scopes"].append(scope)
+        if form_data.client_id:
+            data["client_id"] = form_data.client_id
+        if form_data.client_secret:
+            data["client_secret"] = form_data.client_secret
+        return data
+    ```
+
+    Note that for OAuth2 the scope `items:read` is a single scope in an opaque string.
+    You could have custom internal logic to separate it by colon characters (`:`) or
+    similar, and get the two parts `items` and `read`. Many applications do that to
+    group and organize permissions, you could do it as well in your application, just
+    know that that it is application specific, it's not part of the specification.
+    """
+
+    def __init__(
+        self,
+        *,
+        grant_type: Annotated[
+            Union[str, None],
+            Form(pattern="^password$"),
+            Doc(
+                """
+                The OAuth2 spec says it is required and MUST be the fixed string
+                "password". Nevertheless, this dependency class is permissive and
+                allows not passing it. If you want to enforce it, use instead the
+                `OAuth2PasswordRequestFormStrict` dependency.
+                """
+            ),
+        ] = None,
+        username: Annotated[
+            str,
+            Form(),
+            Doc(
+                """
+                `username` string. The OAuth2 spec requires the exact field name
+                `username`.
+                """
+            ),
+        ],
+        password: Annotated[
+            str,
+            Form(),
+            Doc(
+                """
+                `password` string. The OAuth2 spec requires the exact field name
+                `password".
+                """
+            ),
+        ],
+        scope: Annotated[
+            str,
+            Form(),
+            Doc(
+                """
+                A single string with actually several scopes separated by spaces. Each
+                scope is also a string.
+
+                For example, a single string with:
+
+                ```python
+                "items:read items:write users:read profile openid"
+                ````
+
+                would represent the scopes:
+
+                * `items:read`
+                * `items:write`
+                * `users:read`
+                * `profile`
+                * `openid`
+                """
+            ),
+        ] = "",
+        client_id: Annotated[
+            Union[str, None],
+            Form(),
+            Doc(
+                """
+                If there's a `client_id`, it can be sent as part of the form fields.
+                But the OAuth2 specification recommends sending the `client_id` and
+                `client_secret` (if any) using HTTP Basic auth.
+                """
+            ),
+        ] = None,
+        client_secret: Annotated[
+            Union[str, None],
+            Form(),
+            Doc(
+                """
+                If there's a `client_password` (and a `client_id`), they can be sent
+                as part of the form fields. But the OAuth2 specification recommends
+                sending the `client_id` and `client_secret` (if any) using HTTP Basic
+                auth.
+                """
+            ),
+        ] = None,
+    ):
+        self.grant_type = grant_type
+        self.username = username
+        self.password = password
+        self.scopes = scope.split()
+        self.client_id = client_id
+        self.client_secret = client_secret
+
+
+class OAuth2PasswordRequestFormStrict(OAuth2PasswordRequestForm):
+    """
+    This is a dependency class to collect the `username` and `password` as form data
+    for an OAuth2 password flow.
+
+    The OAuth2 specification dictates that for a password flow the data should be
+    collected using form data (instead of JSON) and that it should have the specific
+    fields `username` and `password`.
+
+    All the initialization parameters are extracted from the request.
+
+    The only difference between `OAuth2PasswordRequestFormStrict` and
+    `OAuth2PasswordRequestForm` is that `OAuth2PasswordRequestFormStrict` requires the
+    client to send the form field `grant_type` with the value `"password"`, which
+    is required in the OAuth2 specification (it seems that for no particular reason),
+    while for `OAuth2PasswordRequestForm` `grant_type` is optional.
+
+    Read more about it in the
+    [FastAPI docs for Simple OAuth2 with Password and Bearer](https://fastapi.tiangolo.com/tutorial/security/simple-oauth2/).
+
+    ## Example
+
+    ```python
+    from typing import Annotated
+
+    from fastapi import Depends, FastAPI
+    from fastapi.security import OAuth2PasswordRequestForm
+
+    app = FastAPI()
+
+
+    @app.post("/login")
+    def login(form_data: Annotated[OAuth2PasswordRequestFormStrict, Depends()]):
+        data = {}
+        data["scopes"] = []
+        for scope in form_data.scopes:
+            data["scopes"].append(scope)
+        if form_data.client_id:
+            data["client_id"] = form_data.client_id
+        if form_data.client_secret:
+            data["client_secret"] = form_data.client_secret
+        return data
+    ```
+
+    Note that for OAuth2 the scope `items:read` is a single scope in an opaque string.
+    You could have custom internal logic to separate it by colon characters (`:`) or
+    similar, and get the two parts `items` and `read`. Many applications do that to
+    group and organize permissions, you could do it as well in your application, just
+    know that that it is application specific, it's not part of the specification.
+
+
+    grant_type: the OAuth2 spec says it is required and MUST be the fixed string "password".
+        This dependency is strict about it. If you want to be permissive, use instead the
+        OAuth2PasswordRequestForm dependency class.
+    username: username string. The OAuth2 spec requires the exact field name "username".
+    password: password string. The OAuth2 spec requires the exact field name "password".
+    scope: Optional string. Several scopes (each one a string) separated by spaces. E.g.
+        "items:read items:write users:read profile openid"
+    client_id: optional string. OAuth2 recommends sending the client_id and client_secret (if any)
+        using HTTP Basic auth, as: client_id:client_secret
+    client_secret: optional string. OAuth2 recommends sending the client_id and client_secret (if any)
+        using HTTP Basic auth, as: client_id:client_secret
+    """
+
+    def __init__(
+        self,
+        grant_type: Annotated[
+            str,
+            Form(pattern="^password$"),
+            Doc(
+                """
+                The OAuth2 spec says it is required and MUST be the fixed string
+                "password". This dependency is strict about it. If you want to be
+                permissive, use instead the `OAuth2PasswordRequestForm` dependency
+                class.
+                """
+            ),
+        ],
+        username: Annotated[
+            str,
+            Form(),
+            Doc(
+                """
+                `username` string. The OAuth2 spec requires the exact field name
+                `username`.
+                """
+            ),
+        ],
+        password: Annotated[
+            str,
+            Form(),
+            Doc(
+                """
+                `password` string. The OAuth2 spec requires the exact field name
+                `password".
+                """
+            ),
+        ],
+        scope: Annotated[
+            str,
+            Form(),
+            Doc(
+                """
+                A single string with actually several scopes separated by spaces. Each
+                scope is also a string.
+
+                For example, a single string with:
+
+                ```python
+                "items:read items:write users:read profile openid"
+                ````
+
+                would represent the scopes:
+
+                * `items:read`
+                * `items:write`
+                * `users:read`
+                * `profile`
+                * `openid`
+                """
+            ),
+        ] = "",
+        client_id: Annotated[
+            Union[str, None],
+            Form(),
+            Doc(
+                """
+                If there's a `client_id`, it can be sent as part of the form fields.
+                But the OAuth2 specification recommends sending the `client_id` and
+                `client_secret` (if any) using HTTP Basic auth.
+                """
+            ),
+        ] = None,
+        client_secret: Annotated[
+            Union[str, None],
+            Form(),
+            Doc(
+                """
+                If there's a `client_password` (and a `client_id`), they can be sent
+                as part of the form fields. But the OAuth2 specification recommends
+                sending the `client_id` and `client_secret` (if any) using HTTP Basic
+                auth.
+                """
+            ),
+        ] = None,
+    ):
+        super().__init__(
+            grant_type=grant_type,
+            username=username,
+            password=password,
+            scope=scope,
+            client_id=client_id,
+            client_secret=client_secret,
+        )
+
+
+class OAuth2(SecurityBase):
+    """
+    This is the base class for OAuth2 authentication, an instance of it would be used
+    as a dependency. All other OAuth2 classes inherit from it and customize it for
+    each OAuth2 flow.
+
+    You normally would not create a new class inheriting from it but use one of the
+    existing subclasses, and maybe compose them if you want to support multiple flows.
+
+    Read more about it in the
+    [FastAPI docs for Security](https://fastapi.tiangolo.com/tutorial/security/).
+    """
+
+    def __init__(
+        self,
+        *,
+        flows: Annotated[
+            Union[OAuthFlowsModel, Dict[str, Dict[str, Any]]],
+            Doc(
+                """
+                The dictionary of OAuth2 flows.
+                """
+            ),
+        ] = OAuthFlowsModel(),
+        scheme_name: Annotated[
+            Optional[str],
+            Doc(
+                """
+                Security scheme name.
+
+                It will be included in the generated OpenAPI (e.g. visible at `/docs`).
+                """
+            ),
+        ] = 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 Authorization 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,
+    ):
+        self.model = OAuth2Model(
+            flows=cast(OAuthFlowsModel, flows), description=description
+        )
+        self.scheme_name = scheme_name or self.__class__.__name__
+        self.auto_error = auto_error
+
+    async def __call__(self, request: Request) -> Optional[str]:
+        authorization = request.headers.get("Authorization")
+        if not authorization:
+            if self.auto_error:
+                raise HTTPException(
+                    status_code=HTTP_403_FORBIDDEN, detail="Not authenticated"
+                )
+            else:
+                return None
+        return authorization
+
+
+class OAuth2PasswordBearer(OAuth2):
+    """
+    OAuth2 flow for authentication using a bearer token obtained with a password.
+    An instance of it would be used as a dependency.
+
+    Read more about it in the
+    [FastAPI docs for Simple OAuth2 with Password and Bearer](https://fastapi.tiangolo.com/tutorial/security/simple-oauth2/).
+    """
+
+    def __init__(
+        self,
+        tokenUrl: Annotated[
+            str,
+            Doc(
+                """
+                The URL to obtain the OAuth2 token. This would be the *path operation*
+                that has `OAuth2PasswordRequestForm` as a dependency.
+                """
+            ),
+        ],
+        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 Authorization 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(
+            password=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
+        return param
+
+
+class OAuth2AuthorizationCodeBearer(OAuth2):
+    """
+    OAuth2 flow for authentication using a bearer token obtained with an OAuth2 code
+    flow. An instance of it would be used as a dependency.
+    """
+
+    def __init__(
+        self,
+        authorizationUrl: str,
+        tokenUrl: Annotated[
+            str,
+            Doc(
+                """
+                The URL to obtain the OAuth2 token.
+                """
+            ),
+        ],
+        refreshUrl: Annotated[
+            Optional[str],
+            Doc(
+                """
+                The URL to refresh the token and obtain a new one.
+                """
+            ),
+        ] = None,
+        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 Authorization 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(
+            authorizationCode=cast(
+                Any,
+                {
+                    "authorizationUrl": authorizationUrl,
+                    "tokenUrl": tokenUrl,
+                    "refreshUrl": refreshUrl,
+                    "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
+    obtain the OAuth2 scopes required by all the dependencies in the same chain.
+
+    This way, multiple dependencies can have different scopes, even when used in the
+    same *path operation*. And with this, you can access all the scopes required in
+    all those dependencies in a single place.
+
+    Read more about it in the
+    [FastAPI docs for OAuth2 scopes](https://fastapi.tiangolo.com/advanced/security/oauth2-scopes/).
+    """
+
+    def __init__(
+        self,
+        scopes: Annotated[
+            Optional[List[str]],
+            Doc(
+                """
+                This will be filled by FastAPI.
+                """
+            ),
+        ] = None,
+    ):
+        self.scopes: Annotated[
+            List[str],
+            Doc(
+                """
+                The list of all the scopes required by dependencies.
+                """
+            ),
+        ] = scopes or []
+        self.scope_str: Annotated[
+            str,
+            Doc(
+                """
+                All the scopes required by all the dependencies in a single string
+                separated by spaces, as defined in the OAuth2 specification.
+                """
+            ),
+        ] = " ".join(self.scopes)