API Key Guard¶
api_key(*, verify, locations, header_name, query_name, cookie_name) — validates API keys delivered in configurable locations.
Signature¶
def api_key(
*,
verify: Callable[[str], AuthUser | None],
locations: Iterable[KeyLocation] = ("header",), # "header" | "query" | "cookie"
header_name: str = "x-api-key",
query_name: str = "api_key",
cookie_name: str = "api_key",
) -> type:
...
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
verify |
callable | — | Sync or async (key) -> AuthUser \| None. |
locations |
iterable | ("header",) |
Where to look, in order. Permitted values: "header", "query", "cookie". The first location yielding a non-empty value wins. |
header_name |
str |
"x-api-key" |
Header name (when "header" is in locations). |
query_name |
str |
"api_key" |
Query parameter name (when "query" is in locations). |
cookie_name |
str |
"api_key" |
Cookie name (when "cookie" is in locations). |
Usage¶
from lauren import LaurenFactory, controller, get, module, use_guards
from lauren_guards import api_key, AuthUser
API_KEYS = {
"key-abc123": AuthUser(id="service-a", scopes=("read",)),
"key-xyz789": AuthUser(id="service-b", scopes=("read", "write")),
}
def verify_key(key: str) -> AuthUser | None:
return API_KEYS.get(key)
ApiKeyGuard = api_key(verify=verify_key)
@use_guards(ApiKeyGuard)
@controller("/v1")
class V1Controller:
@get("/data")
async def data(self) -> dict:
return {"payload": "..."}
@module(controllers=[V1Controller])
class AppModule:
pass
app = LaurenFactory.create(AppModule)
Client usage¶
# Via header (preferred — default locations)
curl -H "X-API-Key: key-abc123" http://localhost:8000/v1/data
To also accept query keys, list both locations:
Prefer header or cookie locations: keys in query strings end up in access logs at every hop.
Key rotation¶
Since verify is a plain callable you can implement any lookup strategy — database, Redis cache, environment variables — without changing guard configuration. Combine with generate_token to mint new keys.