|
from typing import Optional |
|
|
|
from fastapi.openapi.models import APIKey, APIKeyIn |
|
from fastapi.security.base import SecurityBase |
|
from starlette.exceptions import HTTPException |
|
from starlette.requests import Request |
|
from starlette.status import HTTP_403_FORBIDDEN |
|
from typing_extensions import Annotated, Doc |
|
|
|
|
|
class APIKeyBase(SecurityBase): |
|
pass |
|
|
|
|
|
class APIKeyQuery(APIKeyBase): |
|
""" |
|
API key authentication using a query parameter. |
|
|
|
This defines the name of the query parameter that should be provided in the request |
|
with the API key and integrates that into the OpenAPI documentation. It extracts |
|
the key value sent in the query parameter automatically and provides it as the |
|
dependency result. But it doesn't define how to send that API key to the client. |
|
|
|
## Usage |
|
|
|
Create an instance object and use that object as the dependency in `Depends()`. |
|
|
|
The dependency result will be a string containing the key value. |
|
|
|
## Example |
|
|
|
```python |
|
from fastapi import Depends, FastAPI |
|
from fastapi.security import APIKeyQuery |
|
|
|
app = FastAPI() |
|
|
|
query_scheme = APIKeyQuery(name="api_key") |
|
|
|
|
|
@app.get("/items/") |
|
async def read_items(api_key: str = Depends(query_scheme)): |
|
return {"api_key": api_key} |
|
``` |
|
""" |
|
|
|
def __init__( |
|
self, |
|
*, |
|
name: Annotated[ |
|
str, |
|
Doc("Query parameter name."), |
|
], |
|
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 the query parameter is not provided, `APIKeyQuery` will |
|
automatically cancel the request and send the client an error. |
|
|
|
If `auto_error` is set to `False`, when the query parameter 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, in a query |
|
parameter or in an HTTP Bearer token). |
|
""" |
|
), |
|
] = True, |
|
): |
|
self.model: APIKey = APIKey( |
|
**{"in": APIKeyIn.query}, |
|
name=name, |
|
description=description, |
|
) |
|
self.scheme_name = scheme_name or self.__class__.__name__ |
|
self.auto_error = auto_error |
|
|
|
async def __call__(self, request: Request) -> Optional[str]: |
|
api_key = request.query_params.get(self.model.name) |
|
if not api_key: |
|
if self.auto_error: |
|
raise HTTPException( |
|
status_code=HTTP_403_FORBIDDEN, detail="Not authenticated" |
|
) |
|
else: |
|
return None |
|
return api_key |
|
|
|
|
|
class APIKeyHeader(APIKeyBase): |
|
""" |
|
API key authentication using a header. |
|
|
|
This defines the name of the header that should be provided in the request with |
|
the API key and integrates that into the OpenAPI documentation. It extracts |
|
the key value sent in the header automatically and provides it as the dependency |
|
result. But it doesn't define how to send that key to the client. |
|
|
|
## Usage |
|
|
|
Create an instance object and use that object as the dependency in `Depends()`. |
|
|
|
The dependency result will be a string containing the key value. |
|
|
|
## Example |
|
|
|
```python |
|
from fastapi import Depends, FastAPI |
|
from fastapi.security import APIKeyHeader |
|
|
|
app = FastAPI() |
|
|
|
header_scheme = APIKeyHeader(name="x-key") |
|
|
|
|
|
@app.get("/items/") |
|
async def read_items(key: str = Depends(header_scheme)): |
|
return {"key": key} |
|
``` |
|
""" |
|
|
|
def __init__( |
|
self, |
|
*, |
|
name: Annotated[str, Doc("Header name.")], |
|
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 the header is not provided, `APIKeyHeader` will |
|
automatically cancel the request and send the client an error. |
|
|
|
If `auto_error` is set to `False`, when the 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, in a header or |
|
in an HTTP Bearer token). |
|
""" |
|
), |
|
] = True, |
|
): |
|
self.model: APIKey = APIKey( |
|
**{"in": APIKeyIn.header}, |
|
name=name, |
|
description=description, |
|
) |
|
self.scheme_name = scheme_name or self.__class__.__name__ |
|
self.auto_error = auto_error |
|
|
|
async def __call__(self, request: Request) -> Optional[str]: |
|
api_key = request.headers.get(self.model.name) |
|
if not api_key: |
|
if self.auto_error: |
|
raise HTTPException( |
|
status_code=HTTP_403_FORBIDDEN, detail="Not authenticated" |
|
) |
|
else: |
|
return None |
|
return api_key |
|
|
|
|
|
class APIKeyCookie(APIKeyBase): |
|
""" |
|
API key authentication using a cookie. |
|
|
|
This defines the name of the cookie that should be provided in the request with |
|
the API key and integrates that into the OpenAPI documentation. It extracts |
|
the key value sent in the cookie automatically and provides it as the dependency |
|
result. But it doesn't define how to set that cookie. |
|
|
|
## Usage |
|
|
|
Create an instance object and use that object as the dependency in `Depends()`. |
|
|
|
The dependency result will be a string containing the key value. |
|
|
|
## Example |
|
|
|
```python |
|
from fastapi import Depends, FastAPI |
|
from fastapi.security import APIKeyCookie |
|
|
|
app = FastAPI() |
|
|
|
cookie_scheme = APIKeyCookie(name="session") |
|
|
|
|
|
@app.get("/items/") |
|
async def read_items(session: str = Depends(cookie_scheme)): |
|
return {"session": session} |
|
``` |
|
""" |
|
|
|
def __init__( |
|
self, |
|
*, |
|
name: Annotated[str, Doc("Cookie name.")], |
|
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 the cookie is not provided, `APIKeyCookie` will |
|
automatically cancel the request and send the client an error. |
|
|
|
If `auto_error` is set to `False`, when the cookie 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, in a cookie or |
|
in an HTTP Bearer token). |
|
""" |
|
), |
|
] = True, |
|
): |
|
self.model: APIKey = APIKey( |
|
**{"in": APIKeyIn.cookie}, |
|
name=name, |
|
description=description, |
|
) |
|
self.scheme_name = scheme_name or self.__class__.__name__ |
|
self.auto_error = auto_error |
|
|
|
async def __call__(self, request: Request) -> Optional[str]: |
|
api_key = request.cookies.get(self.model.name) |
|
if not api_key: |
|
if self.auto_error: |
|
raise HTTPException( |
|
status_code=HTTP_403_FORBIDDEN, detail="Not authenticated" |
|
) |
|
else: |
|
return None |
|
return api_key |
|
|