dlunire / dlauth
Biblioteca de autenticación para DLUnire.
This package is auto-updated.
Last update: 2026-08-02 10:32:43 UTC
README
Guía práctica de extremo a extremo para usar DLAuth en un proyecto real,
desde el login hasta la revocación de sesiones en todos los dispositivos.
1. Instalación
composer require dlunire/dlauth
2. Filosofía en una frase
DLAuth nunca consulta una base de datos ni lee la petición HTTP por su
cuenta. Solo gestiona $_SESSION, decide cuándo toca revalidar contra
una fuente externa (mediante un TTL), y delega el cómo por completo al
programador. Esto la hace agnóstica de cualquier motor de persistencia
(MySQL, PostgreSQL, SQLite, archivo, lo que sea).
3. Crear tu propia clase concreta
DLAuth es abstract — no se instancia directamente. Extiéndela con tu
propia clase:
<?php declare(strict_types=1); namespace App\Auth; use DLUnire\DLAuth\Auth\DLAuth; final class AppAuth extends DLAuth { public function __construct(string $field = '__APP_AUTH__') { parent::__construct(field: $field); $this->set_revalidation_ttl(300); // 5 minutos, opcional (300 es el default) } }
4. Login — crear la sesión (siempre en POST)
Regla de oro: la creación de sesión debe ocurrir exclusivamente en
peticiones POST, nunca en GET/HEAD. Mezclarlas abre la puerta a
session fixation. La separación la garantiza tu enrutador:
DLRoute::post('/login', [LoginController::class, 'create']); // 201 DLRoute::get('/profile', [ProfileController::class, 'show']); // Lectura
Dentro del controlador de login:
final class LoginController extends BaseController { public function create(): void { session_start(); // requerido antes de usar DLAuth $auth = new AppAuth(); // Datos de contexto, opcionales, deben fijarse ANTES de crear la sesión // IMPORTANTE: si no usa DLUnire, es posible que tengas que hacer lo que observas // aquí abajo: $auth->set_ip($_SERVER['REMOTE_ADDR'] ?? '') ->set_uri($_SERVER['REQUEST_URI'] ?? ''); // Si el login incluye 2FA ya resuelto por un proveedor externo // (Google/Microsoft Authenticator, OTP por SMS/email): // $auth->set_two_factor(true)->set_two_factor_token($otp_result->token); $created = $auth->create_session_data([ 'user_id' => $user->id, 'role' => $user->role, ]); if (!$created) { // Ya existía una sesión válida en este campo — comportamiento // "write-once" deliberado de DLAuth. No se sobrescribe. http_response_code(409); return; } http_response_code(201); } }
Qué ocurre internamente al llamar create_session_data():
- Verifica que haya una sesión de PHP activa (
session_start()ya ejecutado); si no, lanzaRuntimeException. - Verifica que no exista ya una sesión válida en este campo (write-once).
- Solo si va a proceder de verdad, ejecuta
session_regenerate_id(true)— mitigación de session fixation: cualquier ID de sesión que el cliente tuviera antes de autenticarse queda invalidado en este preciso momento. - Escribe el registro completo en
$_SESSION(datos, timestamp, un token aleatorio de 512 bits, contexto capturado con losset_*()).
5. Leer la sesión — en cada petición (GET/HEAD)
final class ProfileController extends BaseController { public function show(): void { session_start(); $auth = new AppAuth(); $auth->authenticated(function ($session) { // $session es un DLUnire\DLAuth\Data\SessionData echo "Bienvenido, usuario #{$session->data['user_id']}"; }); $auth->unauthenticated(function () { http_response_code(401); }); } }
authenticated()/unauthenticated() evitan que tengas que comprobar
is_valid_session por tu cuenta en cada controlador — el callback
correspondiente solo se ejecuta si aplica.
6. Verificar un token recibido del cliente
DLAuth nunca lee la petición HTTP directamente. Si necesitas comparar
un hash que el cliente envió (por ejemplo, en un header Authorization
o una cookie separada del PHPSESSID) contra el token guardado en la
sesión, extráelo con DLRoute primero y pásaselo a DLAuth:
$received_hash = $request->get_header('X-Session-Token'); // resuelto por DLRoute if (!$auth->verify_token($received_hash)) { http_response_code(401); return; }
Internamente usa hash_equals(), no === — comparación en tiempo
constante, indispensable porque $received_hash proviene de un cliente
no confiable.
7. Logout — cerrar sesión en este dispositivo
Puede utilizar HTTP POST o HTTP DELETE.
DLRoute::delete('/logout', [LogoutController::class, 'destroy']);
$auth->logout(); // true si había una sesión y se eliminó
8. Cerrar sesión en todos los dispositivos
Este es el flujo más importante para entender la filosofía de DLAuth.
Tú decides de dónde sale el dato de revocación — DLAuth solo
decide cuándo preguntarte, a través del mismo TTL.
use DLUnire\DLAuth\Data\SessionsRemovalRequest; $auth->delete_all_sessions(function () use ($user_id) { // Este callback SOLO se ejecuta si el TTL ya expiró. Mientras la // sesión esté dentro del rango configurado, ni siquiera se invoca — // cero consultas a tu base de datos en el camino caliente. $row = Revoke::where('user_uuid', $user_id)->first(); return (new SessionsRemovalRequest()) ->set_key('revoke') // columna que indica revocación (1/0) ->capture_value($row); });
El contrato es estricto: el callback debe devolver siempre una
instancia de SessionsRemovalRequest. Si devuelves cualquier otra cosa,
DLAuth lanza TypeError de inmediato, en vez de fallar en silencio más
adelante.
Para pruebas, sin tocar ninguna base de datos:
$auth->delete_all_sessions(fn() => (new SessionsRemovalRequest())->set_key('revoke')->capture_value(['revoke' => 1]) );
Flujo completo de decisión dentro de delete_all_sessions():
¿TTL vigente? ──sí──► no se invoca el callback, no se elimina nada
│no
▼
invoca el callback → exige SessionsRemovalRequest
│
▼
¿is_removable() === true? ──no──► no se elimina nada
│sí
▼
delete_session_data()
9. Cookies — Cookie y SameSite
Si necesitas transportar el token de sesión (o cualquier otro valor) en
una cookie separada de PHPSESSID, usa Cookie:
use DLUnire\DLAuth\Context\Cookie; use DLUnire\DLAuth\Enums\SameSite; (new Cookie('session_token', $session->token)) ->set_secure($is_https) // resuelto por DLRoute, no por Cookie ->set_same_site(SameSite::STRICT) ->set_expires(time() + 3600) ->dispatch();
Defaults ya orientados a seguridad: http_only = true siempre, y
same_site = SameSite::LAX si no se configura explícitamente. secure
queda en false por defecto de forma deliberada — Cookie no tiene
visibilidad propia del esquema HTTP/HTTPS de la petición actual; esa
determinación le corresponde a DLRoute.
Consultar o eliminar una cookie existente, sin instanciar nada:
Cookie::has('session_token'); // bool Cookie::get('session_token'); // ?string Cookie::remove('session_token'); // usa los mismos path/domain originales
10. Resumen de responsabilidades
| Pieza | Responsabilidad |
|---|---|
Auth |
Bajo nivel: TTL, lectura/escritura de $_SESSION, puntos de extensión abstractos. |
DLAuth |
Orquestación de alto nivel: authenticated(), logout(), verify_token(), create_session_data() con regeneración de ID. |
SessionData |
DTO inmutable que representa el registro de sesión. |
SessionsRemovalRequest |
DTO configurable que decide si una sesión debe eliminarse, a partir de un array que tú resuelves. |
Cookie / SameSite |
Transporte de valores en cookies HTTP, con defaults seguros. |
| DLRoute | Enrutamiento, separación POST/GET, resolución del esquema HTTP/HTTPS, extracción de headers. |
DLAuth nunca cruza estas fronteras: no consulta bases de datos, no lee
la petición HTTP directamente, no decide el esquema de cifrado del
transporte. Cada pieza hace una sola cosa.
11. Consideraciones de seguridad ya cubiertas
- Entropía del token:
hash('sha384', random_bytes(64))— 512 bits de una CSPRNG. - Comparación en tiempo constante:
verify_token()usahash_equals(). - Mitigación de session fixation:
session_regenerate_id(true)en cada creación de sesión exitosa. - Write-once: una sesión ya creada no puede sobrescribirse por accidente.
- Defaults seguros en cookies:
http_only = truepor defecto. - Separación POST/GET: responsabilidad de DLRoute, refuerza la mitigación de session fixation a nivel de arquitectura.
12. Fuera del alcance de DLAuth (necesario en otras capas)
- CSRF en el formulario que dispara el
POSTde login/logout. - Rate limiting sobre el endpoint de login, para mitigar fuerza bruta.
- Comparación activa de
ip/user_agentcontra la petición actual para detectar reuso de token desde un contexto distinto —SessionDataguarda estos valores, pero no los compara por sí sola.
Estas piezas deben resolverse en DLRoute o en middlewares de la
aplicación consumidora, no dentro de DLAuth.