lilydocs
HTTP APIWebSocketConsumer
Pipeline2 min read

Guards and authorization

Check whether a verified identity may execute a message at the global, controller, or action level.

A guard determines whether a message action may run. Identity middleware verifies credentials; the guard checks that verified Principal's scopes, roles, or object permissions.

Check a scope#

ChatWriteGuard in the complete middleware example looks for the chat:write scope. The current callback signature also includes cancellation:

WsGuard implementation snippet
async fn can_activate(
    &self,
    exchange: &mut WsMessageExchange,
    _: ExecutionCancellation,
) -> Result<(), WebSocketGuardRejection> {
    if exchange.principal().is_some_and(|p| p.has_scope("chat:write")) {
        Ok(())
    } else {
        Err(WebSocketGuardRejection::error(
            WebSocketErrorCode::new("CHAT_FORBIDDEN").expect("sabit kod"),
            "Bu işlem için yetkiniz yok.",
        ).expect("sabit mesaj"))
    }
}

The linked file contains the constructor and imports. This guard does not verify tokens; it uses the Principal published by identity middleware. Add the corresponding service decision if domain objects also require ownership/tenant checks.

Choose the enforcement level#

Registration Scope
.guard::<ChatWriteGuard>() All message plans in the application
#[guard(ChatWriteGuard)] on a controller That controller's actions
#[guard(ChatWriteGuard)] on an action Only that action

A global guard on the WebSocket builder directly establishes enforcement. Do not transfer HTTP guard registration behavior here. Order is global → controller → action. Repeating a concrete type in one effective plan is a build error; independent routes may share the same guard type.

Decision forms#

WebSocketGuardRejection::error(code, message) rejects the message. ack(code, message) requests a correlated safe error ACK; without incoming ACK authority, no new ID is generated. close(...) chooses connection termination. An ACK rejection does not carry arbitrary business payloads.

After 101, an HTTP 403/429 response cannot be produced. Define how the client handles application errors through the outcome contract. The rate limiter uses this guard mechanism.

Services and time#

exchange.service::<T>().await resolves from the active message scope. A guard and action can use the same Scoped service. If authorization calls an external store, account for cancellation and the remaining deadline; do not start a new full message timeout.

Guard plans check for duplicates. We do not document the middleware limit of 64 entries for guards, because current source does not impose that limit on guard plans. Message lifecycle shows the guard's position before payload extraction.

Sources for this page v0.1.0

This guide is based on the reviewed framework commit.

QUICK START
Documentation
Source code