Skip to content

Auth And Security Reference ​

This page documents auth hook types, approval hooks, framework errors, and signed cookies.

Prerequisites ​

Read Security. Auth hooks are your application policy; Quater only defines when they run and what they receive.

python
from quater import (
    ActionApproval,
    ApprovalRequest,
    AuthConfig,
    AuthContext,
    HTTPError,
    ImproperlyConfigured,
    SignedCookieSigner,
)

AuthConfig ​

Added in 0.1.0b1.

One authenticator bound to one or more request surfaces. Pass a list to Quater(auth=[...]); exactly one runs per request, chosen by source.

python
AuthConfig(authenticator: Authenticator, *, surfaces: Iterable[str], name: str | None = None)
FieldTypeDefaultDescription
authenticatorAuthenticatorrequiredReceives the Request; use await request.resolve(resource) after cheap checks when auth needs a resource.
surfacesIterable[str]requiredSurfaces this covers: any of "api", "mcp", "cli". Each surface may be covered by at most one AuthConfig.
namestr | NoneNoneOptional name used in diagnostics.
python
from quater import AuthConfig, AuthContext, Quater, Request


async def authenticate(request: Request) -> AuthContext | None:
    if request.headers.get("authorization") != "Bearer demo-token":
        return None
    return AuthContext(subject="user_123")


app = Quater(auth=[AuthConfig(authenticate, surfaces=["api", "mcp", "cli"])])

AuthContext ​

Added in 0.1.0a1. payload added in 0.1.0b1.

Authenticated identity returned by an authenticator.

python
AuthContext(
    subject: str,
    metadata: Mapping[str, object] = {},
    payload: object = None,
)
FieldTypeDefaultDescription
subjectstrrequiredStable user, service, or agent id.
metadataMapping[str, object]empty read-only mappingSmall request-scoped values your app wants to carry.
payloadobjectNoneAn app object the authenticator already loaded (for example the User row), read back by a handler through a resource with no second query. Quater never inspects it.

Example:

python
from quater import AuthContext, Request


async def authenticate(request: Request) -> AuthContext | None:
    if request.headers.get("authorization") != "Bearer demo-token":
        return None
    return AuthContext(subject="user_123")

ApprovalRequest ​

Added in 0.1.0a1.

Input passed to action_approval for protected MCP tools and CLI actions.

python
ApprovalRequest(
    action: str,
    arguments_hash: str,
    token: str,
    auth: AuthContext | None = None,
    context: RequestContext = RequestContext(),
)
FieldTypeDefaultDescription
actionstrrequiredTool or CLI action name.
arguments_hashstrrequiredSHA-256 hash of the action name and canonical bound arguments.
tokenstrrequiredSubmitted approval token.
authAuthContext | NoneNoneAuthenticated caller when available.
contextRequestContextRequestContext()Source and entrypoint metadata.

ActionApproval ​

Added in 0.1.0a1.

Callable type for approval hooks.

python
ActionApproval = Callable[[ApprovalRequest], Awaitable[bool]]

Return True to allow execution. Return False to deny it.

Authenticator ​

Added in 0.1.0b1.

Callable type for an authenticator. It receives the Request; resource parameters are rejected so no-token requests can fail before opening a database session. Use await request.resolve(resource) after cheap checks when auth needs a resource. Handlers can inject that same resource through Annotated[T, resource].

python
Authenticator = Callable[[Request], Awaitable[AuthContext | None]]

Return AuthContext to allow the request. Return None to deny it (raising HTTPError works too). Returning any other type is treated as unauthorized.

HTTPError ​

Added in 0.1.0a1.

Exception converted into an HTTP-style response.

python
HTTPError(detail: str | None = None, *, status_code: int | None = None)
ParameterTypeDefaultDescription
detailstr | NoneNoneClient-facing error text. Defaults to "Internal Server Error".
status_codeint | NoneNoneResponse status. Defaults to 500.

Example:

python
from quater import HTTPError


@app.get("/orders/{order_id}")
async def get_order(order_id: str) -> dict[str, str]:
    raise HTTPError("Order not found", status_code=404)

Expected response:

text
404 Not Found
Order not found

ImproperlyConfigured ​

Added in 0.1.0a1.

Framework setup error. Catch this when startup should fail loudly.

python
raise ImproperlyConfigured("bad setup")

ConfigurationError remains in quater.exceptions as a compatibility subclass, but new app code should use ImproperlyConfigured.

SignedCookieSigner ​

Added in 0.1.0a1.

HMAC signer for small cookie values. It signs values; it does not encrypt them.

python
SignedCookieSigner(
    secret: str | bytes,
    *,
    fallback_secrets: Iterable[str | bytes] = (),
    salt: str = "quater.cookie",
)
ParameterTypeDefaultDescription
secretstr | bytesrequiredCurrent signing secret.
fallback_secretsIterable[str | bytes]()Old secrets accepted during rotation.
saltstr"quater.cookie"Purpose-specific signing salt.

Methods:

MethodReturnDescription
sign(value)strReturns a signed cookie value.
verify(signed_value)str | NoneReturns original value or None.

Raises ImproperlyConfigured if secrets are empty.

What Can Go Wrong ​

Unauthorized : An auth hook returned None.

Approval required : A protected tool or action ran without a valid approval token.

Approval denied : action_approval returned False.

Signed cookie secrets must not be empty : Provide non-empty current and fallback secrets.

Also See ​

Released under the MIT License.