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.