I have a Python/Quart API REST microservice and want to apply API key based authentication.
What is the conventional way to do this and what is the general way to store API keys?
Can the package do this or do I need to implement my own by manually checking "?api_key=asdfasdf" values etc?
I see quart_auth has basic authentication but not API key based authentication...
There isn't a built in way to do this (Quart is agnostic, and Quart-Auth focuses on cookie and basic authentication). However the following will work for header based API keys,
from quart import (
current_app,
has_request_context,
has_websocket_context,
request,
websocket,
)
from werkzeug.exceptions import Unauthorized
from collections.abc import Callable
from functools import wraps
from typing import Any
def api_key_required(
api_config_key: str = "API_KEY",
) -> Callable:
"""A decorator to restrict route access to requests with an API key.
This should be used to wrap a route handler (or view function) to
enforce that only api key authenticated requests can access it. The
key value is configurable via the app configuration with API_KEY key
used by default. Note that it is important that this decorator be
wrapped by the route decorator and not vice, versa, as below.
.. code-block:: python
@app.route('/')
@api_key_required()
async def index():
...
If the request is not authenticated a
`werkzeug.exceptions.Unauthorized` exception will be raised.
"""
def decorator(func: Callable) -> Callable:
@wraps(func)
async def wrapper(*args: Any, **kwargs: Any) -> Any:
if has_request_context():
api_key = request.headers.get("X-API-Key", "")
elif has_websocket_context():
api_key = websocket.headers.get("X-API-Key", "")
else:
raise RuntimeError("Not used in a valid request/websocket context")
if (compare_digest(api_key, current_app.config[api_config_key])):
return await current_app.ensure_async(func)(*args, **kwargs)
else:
raise Unauthorized()
return wrapper
return decorator
For query string or cookie based API keys request.args
and request.cookies
can be used instead of request.headers
.