Backends de autenticación
Un backend de autenticación responde a una única pregunta: dada una petición
entrante, ¿quién es el usuario? Rustango te permite apilar varios — HTTP
Basic, clave de API, JWT — en una cadena que el middleware de autenticación
prueba en orden, de modo que una misma aplicación puede aceptar humanos y
máquinas en las mismas rutas. Es la idea de AUTHENTICATION_BACKENDS de Django,
cableada a axum. Combínalo con require_auth / require_perm para restringir
rutas y el extractor CurrentUser para leer el resultado.
¿Algún término aquí es nuevo para ti? Backend, middleware, extractor, codename de permiso — consulta el glosario.
Fuente:
rustango::tenancy::auth_backends(AuthBackend,ModelBackend,ApiKeyBackend,JwtBackend,AuthUser,AuthError) yrustango::tenancy::{RouterAuthExt, CurrentUser}— detrás de la característicatenancy. Un registro portable e independiente de la base de datos también vive enrustango::auth_backends(siempre compilado).Versión ejecutable: cada fragmento está copiado de
auth_backends_doc.rs(cargo test -p rustango --features sqlite,tenancy --test auth_backends_doc).
Tabla de contenidos
- La cadena · Los backends integrados
- Restringir rutas: require_auth
- Leer el usuario: CurrentUser
- Permisos: require_perm
- El registro portable
- Véase también
La cadena
Pasas a require_auth un Vec<Arc<dyn AuthBackend>>. En cada petición, el
middleware los prueba en orden:
- el primer backend que reconoce la credencial gana (devuelve el usuario);
- un backend que no la reconoce devuelve «ninguno» y se prueba el siguiente;
- si un backend falla de forma dura (p. ej. una cuenta inactiva con un token válido), la cadena se detiene con ese error;
- si ninguno coincide, la petición recibe
401(conrequire_auth) o continúa de forma anónima (conoptional_auth).
use std::sync::Arc;
use rustango::tenancy::auth_backends::{ApiKeyBackend, AuthBackend, ModelBackend};
let backends: Vec<Arc<dyn AuthBackend>> = vec![
Arc::new(ModelBackend), // HTTP Basic → humans
Arc::new(ApiKeyBackend), // Bearer key → machines
];
Los backends integrados
| Backend | Credencial que lee | Identifica a un usuario por |
|---|---|---|
ModelBackend | Authorization: Basic <base64(user:pass)> | nombre de usuario + verificación de contraseña argon2id contra rustango_users |
ApiKeyBackend | Authorization: Bearer <prefix.secret> | la tabla rustango_api_keys (véase Claves de API) |
JwtBackend | Authorization: Bearer <jwt> | un token HS256 firmado (véase JWT) |
ApiKeyBackend y JwtBackend leen ambos Bearer y desambiguan por la forma (el
primer segmento separado por punto de una clave de API tiene exactamente 8
caracteres). Construye JwtBackend con un secreto de al menos 32 bytes
(JwtBackend::new(secret) entra en pánico en caso contrario):
use rustango::tenancy::auth_backends::JwtBackend;
let backends: Vec<Arc<dyn AuthBackend>> = vec![
Arc::new(ModelBackend),
Arc::new(JwtBackend::new(jwt_secret_at_least_32_bytes.to_vec())),
];
Escribe un backend personalizado implementando el trait (un único método async
que inspecciona los Parts de la petición y devuelve Option<AuthUser>):
use async_trait::async_trait; // add `async-trait` to your Cargo.toml
use axum::http::request::Parts;
use rustango::sql::Pool;
use rustango::tenancy::auth_backends::{AuthBackend, AuthError, AuthUser};
struct HeaderBackend;
#[async_trait]
impl AuthBackend for HeaderBackend {
async fn authenticate(&self, parts: &Parts, _pool: &Pool)
-> Result<Option<AuthUser>, AuthError>
{
// ...inspect parts.headers, return Some(AuthUser{..}) or Ok(None)
Ok(None)
}
}
Restringir rutas: require_auth
RouterAuthExt añade el middleware. require_auth rechaza las peticiones
anónimas con 401; optional_auth las deja pasar (de modo que un handler puede
ramificar según con sesión iniciada o no):
use rustango::tenancy::RouterAuthExt;
let app = Router::new()
.route("/profile", get(profile))
.require_auth(backends, pool); // 401 if no backend matches
Comportamiento verificado:
// no credentials → 401
// Basic alice:<correct> → 200
// Basic alice:<wrong> → 401 (no backend accepted; no enumeration)
// Bearer <valid api key> → 200
Leer el usuario: CurrentUser
Los handlers leen el usuario autenticado con el extractor CurrentUser. Es
infalible — Some(user) cuando un backend resolvió uno, None en caso
contrario:
use rustango::tenancy::CurrentUser;
async fn profile(CurrentUser(user): CurrentUser) -> Response {
match user {
Some(u) => format!("hello {}", u.username).into_response(),
None => StatusCode::UNAUTHORIZED.into_response(),
}
}
Trampa: como
CurrentUseres infalible, olvidarrequire_authno provoca un fallo de compilación — cada petición simplemente veNone. Detrás derequire_auth, las peticiones anónimas ya reciben401, así queusersiempre esSomeahí.
Permisos: require_perm
require_perm restringe una ruta a un codename de permiso
({table}.{action}, p. ej. post.add). Aplícalo al subrouter interno y
require_auth al externo, de modo que el usuario se resuelva antes de
comprobar el permiso:
let admin = Router::new()
.route("/admin", get(admin_only))
.require_perm("post.add", pool.clone()); // inner: needs the codename
let app = Router::new()
.route("/profile", get(profile))
.merge(admin)
.require_auth(backends, pool); // outer: resolves the user first
// alice (granted post.add) → /admin 200
// bob (authed, no grant) → /admin 403
// anonymous → /admin 401 (auth runs first)
Resolución: un superusuario (activo) pasa todo; un usuario desactivado es
denegado incluso con concesiones; una anulación explícita por usuario prevalece
sobre las concesiones de rol; en caso contrario, cualquier rol que el usuario
posea que conceda el codename pasa. Concede con
set_user_perm_pool / los roles mediante create_role_pool + assign_role_pool
(las tablas de permisos las crea ensure_tables_pool).
El registro portable
Por separado, rustango::auth_backends (nota: raíz del crate, no tenancy)
es un pequeño registro independiente del framework — una cadena
Credentials → Principal con su propio trait AuthBackend. No tiene ningún
pegamento HTTP/axum; úsalo cuando quieras una capacidad de conexión de backends
al estilo Django dentro de tu propio código de autenticación:
use rustango::auth_backends::{AuthBackendChain, Credentials, RemoteUserBackend};
let chain = AuthBackendChain::new().with(Arc::new(RemoteUserBackend::trust_username()));
let principal = chain.authenticate(&Credentials::remote("alice")).await?;
La misma semántica «el primer éxito gana / el primer error detiene» que la cadena
HTTP. Para restringir rutas reales, usa el middleware de tenancy de arriba.
Véase también
- Claves de API y JWT — las credenciales que
ApiKeyBackend/JwtBackendconsumen. - Contraseñas — el hashing contra el que
ModelBackendverifica. - Decoradores de acceso — restricción por handler con
login_required/permission_required, la alternativa de estilo decorador arequire_auth/require_perm. - Sesiones — autenticación basada en cookies para navegadores.
