Skip to main content

🛡️ Guards (Authorization)

Guards have a single responsibility: they determine whether a given request will be handled by the route handler or not, depending on certain conditions (like permissions, roles, ACLs, etc.) present at run-time.

In Ferrox, Guards are evaluated right after Middlewares but before Pipes and Controllers.

High-Level Example

To protect a route so that only Admin users can access it, you simply inject the Role Extractor in your controller arguments.

use axum::{Json, routing::get, Router};
use ferrox_security::{AuthPayload, Role};

async fn delete_database(
// The Guard! If the JWT doesn't contain the Admin role, this throws 403 Forbidden
auth: AuthPayload,
) -> Json<&'static str> {

if auth.role != Role::Admin {
// You can also handle it manually, but Ferrox Security Middlewares
// usually prevent the request from even reaching this point.
return Json("Nice try.");
}

Json("Database Deleted!")
}

Low-Level Internal Details

Under the hood, a Guard in Ferrox is simply an implementation of the FromRequestParts trait in Axum.

Unlike FromRequest (which consumes the request body, like a JSON Pipe), FromRequestParts only looks at the HTTP Headers (like the Authorization header). This means multiple Guards can be chained sequentially.

If a Guard fails (e.g., the JWT signature is invalid), it returns a ferrox_errors::AppError::Unauthorized. Because of Ferrox's early-return architecture, the TCP connection is immediately answered with a 401/403 status code, saving database connections and memory.