Skip to content

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:

ApiKeyGuard = api_key(
    verify=verify_key,
    locations=("header", "query"),
    query_name="api_key",
)
curl "http://localhost:8000/v1/data?api_key=key-abc123"

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.