Protege las acciones sensibles
Vuelve a pedir la contraseña o el PIN, fuerza un cambio de contraseña, actúa como un usuario, bloquea una cuenta.
Algunas acciones merecen más que un clic: eliminar una cuenta, cambiar el destino de un pago, iniciar sesión en nombre de alguien más. El kit trae cinco mecanismos para ellas: un modal de confirmación que puedes envolver alrededor de cualquier acción Livewire, una pantalla de reconfirmación de contraseña, un cambio de contraseña forzado, la suplantación desde el panel de administración y el bloqueo de cuentas. Esta guía dice cómo funciona cada uno, quién puede activarlo, qué ve la persona del otro lado y cómo engancharlo a acciones tuyas.
Vuelve a pedir la contraseña o el PIN
App\Traits\ConfirmsUserActions convierte cualquier método de un componente Livewire en uno que corre sólo después de que el usuario demuestra quién es. Agrega el trait y, en lugar de hacer el trabajo directamente, pide confirmación y nombra el método que lo hace:
use App\Traits\ConfirmsUserActions;
use Livewire\Component;
class DeleteAccount extends Component
{
use ConfirmsUserActions;
public function delete()
{
$this->requiresUserConfirmation('performDeletion', ['accountId' => 123]);
}
public function performDeletion($user, $accountId)
{
// Corre sólo después de un PIN o contraseña correctos.
// $user es el App\Models\User autenticado.
}
}
requiresUserConfirmation($method, $params) abre el modal App\Livewire\App\Actions\ConfirmUserAction, titulado "Confirmar acción". Lo que pide depende de la configuración del PIN:
| Situación | El modal pide |
|---|---|
AUTH_ENABLE_PIN=true y el usuario ya configuró un PIN |
El PIN de 4 dígitos |
| En cualquier otro caso | La contraseña de la cuenta |
| Nadie tiene sesión iniciada | Además la identidad (correo, teléfono o usuario, según LOGIN_IDENTITIES) |
Cuando la credencial es correcta, el modal dispara un evento userActionConfirmed y se cierra. El trait lo escucha, verifica que el evento iba dirigido exactamente a esta clase de componente, carga al usuario y llama a tu método con el usuario como primer argumento seguido de los parámetros que pasaste, en orden. Un PIN o contraseña incorrectos muestran "Credenciales inválidas." debajo del campo y el modal sigue abierto; Cancelar lo cierra y nada corre.
Dos detalles importan al conectarlo. El componente debe extender Livewire\Component y no debe sobrescribir getListeners() sin llamar al del trait, porque así es como regresa la confirmación. Y el nombre del método se verifica con method_exists, así que un error de dedo no corre nada en lugar de fallar.
Para abrir el mismo modal desde Blade o Alpine en lugar de desde PHP, el helper global confirmUserAction($component, $method, $params) devuelve el par component y arguments que el evento openModal espera:
<x-button negative :label="__('Delete')"
x-on:click="$dispatch('openModal', @js(confirmUserAction(\App\Livewire\App\DeleteAccount::class, 'performDeletion', ['accountId' => 123])))" />
El kit trae el mecanismo pero hoy no lo usa en ninguna de sus propias pantallas, así que nada cambia hasta que envuelvas una acción. Cómo se configura, guarda y restablece el PIN está en El PIN de seguridad.
La pantalla de confirmación de contraseña
Aparte del modal, el kit tiene la clásica página de reconfirmación en /password/confirm, la ruta password.confirm, renderizada por App\Livewire\Auth\Access\ConfirmPassword. Muestra el icono, un título "Confirmar contraseña" y un campo; una contraseña correcta escribe la hora actual en la sesión como auth.password_confirmed_at y regresa al usuario a la página que quería.
Esa marca de tiempo es lo que lee el middleware password.confirm de Laravel. Ponlo en cualquier ruta que deba volver a pedir la contraseña pasado un tiempo:
Route::livewire('/billing/payout', Billing\Payout::class)
->middleware('password.confirm');
| Opción | Variable de entorno | Valor por omisión | Efecto |
|---|---|---|---|
password_timeout en config/auth.php |
AUTH_PASSWORD_TIMEOUT |
10800 (tres horas) |
Segundos desde la última confirmación antes de que el middleware redirija a la pantalla |
Iniciar sesión también escribe la marca de tiempo, así que a un usuario no se le pide dos veces dentro de la ventana. Ninguna ruta del kit aplica el middleware, así que la pantalla y la variable están disponibles pero sin uso hasta que lo agregues a una ruta tuya.
Fuerza un cambio de contraseña
Un administrador, o tu código, puede exigir a un usuario que elija una contraseña nueva antes de hacer cualquier otra cosa. El mecanismo es App\Traits\EnforcesPasswordRequests en el modelo User y dos marcas de tiempo:
| Columna | Significado |
|---|---|
requested_password_reset_at |
Cuándo se exigió el cambio |
password_reset_at |
Cuándo cambió el usuario su contraseña por última vez a través de la pantalla forzada |
shouldEnforcePasswordRequest() es verdadero mientras la solicitud sea más reciente que el cambio. Tres cosas fijan la solicitud:
- Un administrador crea un usuario con contraseña en
/admin/users. El usuario recibe un correo con esa contraseña y correrequestForNewPassword(), así que el primer inicio de sesión la reemplaza. php artisan db:seedencuentra a un superadministrador que todavía usa la contraseña sembrada.- Tu propio código llama a
$user->requestForNewPassword().
La aplicación la hace el middleware security descrito abajo. En la siguiente petición a un área protegida redirige a /password/request, una página titulada "Cambia tu contraseña" que dice "Se te ha solicitado que cambies tu contraseña". El usuario escribe la contraseña actual y presiona Continuar, luego una contraseña nueva y su confirmación y presiona Confirmar. La contraseña nueva debe pasar la regla de contraseñas del proyecto y ser distinta de la actual; entonces se sella password_reset_at, se dispara el evento PasswordReset de Laravel y el usuario cae en la página a la que iba, o en home_route. Abrir la página cuando no hay cambio pendiente responde 400.
El middleware de seguridad
App\Http\Middleware\Security está registrado con el alias security y envuelve las áreas con sesión: cada ruta bajo /app, cada ruta bajo /admin y las páginas de cuenta bajo /account. En cada petición hace dos cosas, en este orden:
- Si el usuario debe cambiar su contraseña, redirige a la pantalla de cambio forzado, salvo que el usuario sea un administrador actuando como alguien más.
- Si el
blocked_atdel usuario tiene valor, cierra su sesión y lo manda a la página de inicio de sesión con "Estás bloqueado, por favor contacta al soporte."
Eso es todo lo que hace. No fija cabeceras de seguridad ni fuerza HTTPS; la aplicación confía en cualquier proxy para el esquema y el host, y la redirección a HTTPS y las cabeceras como Strict-Transport-Security le corresponden a tu servidor web o CDN. Agrégalo a cualquier grupo de rutas tuyo al que llegue un usuario con sesión:
Route::middleware(['web', 'auth', 'security'])->group(function () {
// ...
});
Un segundo middleware, App\Http\Middleware\SystemValidations, corre en cada petición web y deja las advertencias que <x-system-warnings /> muestra en la parte superior del área con sesión. Hoy tiene una: cuando AUTH_ENABLE_PIN=true y el usuario no tiene PIN, un banner ámbar dice "Necesitas configurar un PIN para tu cuenta." con un enlace Configurar PIN al perfil. Dónde se define cada grupo de middleware está en Rutas, layouts y middleware.
Actúa como otro usuario
La suplantación permite a un administrador ver el producto exactamente como lo ve un usuario, para reproducir un reporte sin pedir una contraseña. Es el trait App\Traits\CanActAsOthers en el modelo User, expuesto como la acción "Actuar como" en la lista de usuarios y en el detalle de usuario de /admin/users.
Quién puede: cualquier administrador con sesión que pueda abrir /admin/users. La única verificación es que el objetivo sea una cuenta distinta; no se requiere ser sudo, y el objetivo puede ser cualquiera, un superadministrador incluido. Dale el rol de administrador a personas a las que confiarías todas las cuentas.
Qué pasa:
actAs($target)guarda en la sesión el id del administrador, su clase de modelo y su estado de recordarme bajocurrentUserSimulating, inicia sesión con el objetivo sin recordarme y redirige ahome_route.- Mientras actúa, el menú de usuario muestra "Dejar de actuar" en lugar de "Cerrar sesión". La redirección al cambio de contraseña forzado se omite, así que un administrador puede ayudar a un usuario que tiene uno pendiente. Todo lo demás se comporta como para ese usuario, y el registro de actividad anota lo que se hace bajo la cuenta suplantada, porque esa es la que tiene la sesión.
- "Dejar de actuar" llega a
/stop-acting, que cierra la sesión del objetivo, quita la clave de sesión, vuelve a iniciar sesión con el administrador con el estado de recordarme que tenía y redirige ahome_route. Si la cuenta del administrador ya no existe, la sesión simplemente termina cerrada.
En código el trait ofrece canActAs($user), actAs($user), isActing() y stopActingAs(), todos para llamarse sobre el usuario con sesión actual. Nada escribe en el registro de actividad cuando la suplantación empieza o termina; el único rastro es una alerta en el log de la aplicación si una sesión intenta regresar a un administrador que no pudo haberla iniciado.
Bloquea una cuenta
Bloquear conserva la cuenta y sus datos pero impide que la persona la use. Es una sola marca de tiempo anulable, blocked_at en la tabla de usuarios, y una acción de administrador.
- En
/admin/users, abre un usuario y elige "Bloquear usuario". La misma acción desbloquea una cuenta bloqueada. No se ofrece sobre usuarios que tengan el rol de administrador, así que un administrador no puede dejar fuera a otro. La página de detalle muestra "Bloqueado el" una vez fijado. - Desde código:
$user->update(['blocked_at' => now()]), ynullpara levantarlo.
Un usuario bloqueado todavía puede enviar el formulario de inicio de sesión: las credenciales se verifican y la sesión se crea. En la siguiente petición a /app el middleware security cierra su sesión y lo regresa a la página de inicio de sesión con "Estás bloqueado, por favor contacta al soporte." Una sesión que ya estaba abierta se cierra de la misma forma en su siguiente petición. Los correos de avisos se saltan las cuentas bloqueadas. Eliminar a un usuario desde el panel de administración es un borrado suave, que también impide iniciar sesión, pero bloquear es la opción reversible.
Dos cosas que el kit no hace: nunca bloquea una cuenta por sí solo tras intentos fallidos de inicio de sesión, y el formulario de inicio de sesión no tiene límite de intentos. Si tu producto necesita cualquiera de las dos, agrega una verificación con RateLimiter al componente de inicio de sesión o un middleware de throttle en la ruta.
El registro de actividad
Los cambios a usuarios, roles, permisos, avisos, categorías y los modelos de cobro quedan registrados por spatie/laravel-activitylog y los superadministradores pueden consultarlos en /admin/activities, una fila por evento con los valores anteriores y nuevos. ACTIVITY_LOGGER_ENABLED=false detiene el registro y ACTIVITY_LOGGER_DB_CONNECTION manda la tabla a otra conexión. Qué se registra, cómo agregar un modelo y cómo leer la diferencia está en Rastros de auditoría y eventos de webhook.