Autenticación de usuarios
1Introducción
Esta documentación explica paso a paso cómo configurar un sistema de autenticación sin contraseña en una aplicación Temma.
El principio es simple: el usuario introduce su dirección de correo electrónico, recibe un enlace mágico por correo, y al hacer clic en él, queda autenticado. El enlace solo puede usarse una vez y caduca al cabo de una hora.
Este sistema se apoya en dos componentes proporcionados por Temma:
- El controlador/plugin \Temma\Controllers\Auth: gestiona el formulario de inicio de sesión, el envío de correos y la sesión del usuario.
- El atributo \Temma\Attributes\Auth: restringe el acceso a controladores y acciones según la autenticación, los roles y los servicios.
2Requisitos previos
Para seguir este tutorial, necesitas:
- Un proyecto Temma en funcionamiento (consulta la página de instalación).
- Una base de datos MySQL o MariaDB configurada en tus fuentes de datos.
- Un servidor capaz de enviar correos electrónicos (para los enlaces mágicos).
3Creación de la base de datos
El sistema de autenticación requiere dos tablas: User (usuarios) y AuthToken (tokens de conexión enviados por correo).
Ejecuta las siguientes consultas SQL para crear estas tablas:
CREATE TABLE User (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
date_creation DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
date_last_login DATETIME,
date_last_access DATETIME,
email TINYTEXT CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
name TINYTEXT,
roles SET('admin', 'writer', 'reviewer'), -- debe personalizarse
services SET('articles', 'news', 'images'), -- debe personalizarse
PRIMARY KEY (id),
UNIQUE INDEX email (email(255))
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
CREATE TABLE AuthToken (
token CHAR(64) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
expiration DATETIME NOT NULL,
user_id INT UNSIGNED NOT NULL,
PRIMARY KEY (token),
INDEX expiration (expiration),
FOREIGN KEY (user_id) REFERENCES User (id) ON DELETE CASCADE
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
Los campos roles y services de la tabla User son de tipo SET: debes personalizarlos según las necesidades de tu aplicación. Los roles representan las funciones del usuario (administrador, redactor, etc.), y los services representan los módulos a los que tiene acceso (artículos, imágenes, etc.).
4Configuración
Debes configurar \Temma\Controllers\Auth tanto como pre-plugin (para gestionar la sesión del usuario en cada petición) como ruta (para que el formulario de inicio de sesión sea accesible).
Añade lo siguiente a tu archivo etc/temma.php:
<?php
return [
'application' => [
'dataSources' => [
'db' => 'mysql://user:password@localhost/myDatabase'
]
],
'plugins' => [
'_pre' => [
'\Temma\Controllers\Auth'
]
],
'routes' => [
'auth' => '\Temma\Controllers\Auth'
]
];
- Líneas 5 a 7: Configuración de la conexión a la base de datos (adáptala a tu entorno).
- Líneas 10 a 12: Declaración del pre-plugin. En cada petición, comprueba si el usuario está autenticado y pone su información a disposición.
- Líneas 14 a 16: Declaración de la ruta. El controlador será accesible mediante las URLs /auth/login y /auth/logout.
5Personalización de la plantilla de inicio de sesión
El controlador Auth usa una plantilla Smarty ubicada en templates/auth/login.tpl. La versión proporcionada por Temma es mínima; te recomendamos crear tu propia versión.
Aquí tienes un ejemplo de plantilla de formulario de inicio de sesión:
<html>
<head>
<title>Login</title>
</head>
<body>
<h1>Login</h1>
{if $__authStatus == 'tokenSent'}
<p style="color: green;">
Se ha enviado un enlace de inicio de sesión a tu dirección de correo.
Revisa tu bandeja de entrada.
</p>
{else}
{if $__authStatus}
<p style="color: red;">Se ha producido un error, inténtalo de nuevo.</p>
{/if}
<form method="post" action="/auth/authentication">
<label for="email">Dirección de correo:</label>
<input type="email" id="email" name="email" required />
<button type="submit">Iniciar sesión</button>
</form>
{/if}
</body>
</html>
- Línea 7: la variable flash $__authStatus vale 'tokenSent' en cuanto se ha enviado correctamente un enlace de inicio de sesión; se muestra entonces un mensaje de confirmación en lugar del formulario.
- Líneas 13 a 15: para cualquier otro estado (dirección de correo inválida, límite de intentos alcanzado, fallo del control anti-robot...), se muestra un mensaje de error genérico. Consulta la documentación de referencia del controlador Auth para la lista completa de estados posibles.
- Líneas 16 a 19: El formulario envía la dirección de correo mediante POST a /auth/authentication.
Por defecto, el envío de este formulario también requiere un campo hash, calculado mediante JavaScript, para filtrar robots (consulta la documentación de referencia para el algoritmo). Para mantener este tutorial simple, desactivemos este control:
<?php
return [
'x-security' => [
'auth' => [
'robotCheckDisabled' => true
]
]
];
Elimina esta opción en producción, e implementa el mecanismo anti-robot descrito en la documentación de referencia.
6Prueba del inicio de sesión
Antes de hacer la prueba, asegúrate de tener al menos un usuario en la base de datos. Puedes insertar uno manualmente:
INSERT INTO User (email, name) VALUES ('john@example.com', 'John');
El proceso de inicio de sesión funciona así:
- Ve a /auth/login en tu navegador.
- Introduce la dirección de correo del usuario y envía el formulario.
- Se envía un correo con un enlace de inicio de sesión a esa dirección.
- Al hacer clic en el enlace, el usuario queda autenticado y es redirigido a la página de inicio.
El enlace de inicio de sesión es de un solo uso y caduca al cabo de una hora.
7Protección de páginas con el atributo Auth
El atributo Auth restringe el acceso a controladores o acciones según la autenticación. Se apoya en la variable de plantilla $currentUser definida por el pre-plugin.
Para proteger un controlador completo (todas sus acciones requieren autenticación):
use \Temma\Attributes\Auth as TµAuth;
#[TµAuth]
class Account extends \Temma\Web\Controller {
public function profile() {
// accesible solo para usuarios autenticados
}
public function settings() {
// ídem
}
}
Para proteger solo algunas acciones específicas:
use \Temma\Attributes\Auth as TµAuth;
class Blog extends \Temma\Web\Controller {
// accesible para todos
public function list() { }
// restringido a usuarios con el rol "writer"
#[TµAuth('writer')]
public function create() { }
// restringido a usuarios con acceso al servicio "images"
#[TµAuth(service: 'images')]
public function uploadImage() { }
// restringido a usuarios con el rol "admin" o "writer"
#[TµAuth(['admin', 'writer'])]
public function edit() { }
}
Por defecto, un usuario no autenticado que intenta acceder a una página protegida recibe un error HTTP 401; un usuario autenticado que no tiene el rol o el acceso al servicio requerido recibe en cambio un error HTTP 403. Para redirigirlo al formulario de inicio de sesión en ambos casos, añade esta configuración a etc/temma.php:
<?php
return [
'x-security' => [
'authRedirect' => '/auth/login'
]
];
Para guardar la URL solicitada de modo que el usuario sea redirigido a ella después de iniciar sesión, usa el parámetro storeUrl:
use \Temma\Attributes\Auth as TµAuth;
#[TµAuth(redirect: '/auth/login', storeUrl: true)]
class Account extends \Temma\Web\Controller {
// ...
}
De esta forma, tras iniciar sesión, el usuario será redirigido a la página que intentaba visitar.
8Uso de la información del usuario
El pre-plugin Auth proporciona dos variables de plantilla, accesibles en plantillas, controladores y plugins:
- currentUserId: Identificador del usuario autenticado (o null si no está autenticado).
- currentUser: Datos del usuario (array asociativo que contiene las claves id, email, name, roles, services, etc.).
Aquí tienes un ejemplo de uso en una plantilla Smarty:
{if $currentUserId}
<p>Hola, {$currentUser.name}!</p>
{if $currentUser.roles.admin}
<a href="/admin">Administración</a>
{/if}
<a href="/auth/logout">Logout</a>
{else}
<a href="/auth/login">Login</a>
{/if}
En un controlador, estas variables son accesibles mediante $this['currentUserId'] y $this['currentUser']:
class Account extends \Temma\Web\Controller {
public function profile() {
$userId = $this['currentUserId'];
$user = $this['currentUser'];
$this['userName'] = $user['name'];
$this['isAdmin'] = $user['roles']['admin'] ?? false;
}
}
9Cierre de sesión
Para cerrar la sesión de un usuario, redirígelo a la URL /auth/logout. Será redirigido automáticamente a la página de inicio tras cerrar sesión.
Ejemplo de enlace de cierre de sesión en una plantilla:
<a href="/auth/logout">Logout</a>
Si quieres redirigir al usuario a una URL específica tras iniciar sesión (y por lo tanto también tras cerrar sesión y volver a iniciarla), configura el parámetro redirection (consulta la sección Configuración avanzada).
10Configuración avanzada
Todas las opciones de configuración se sitúan bajo la clave x-security > auth en el archivo etc/temma.php.
Personalización del correo electrónico
Puedes personalizar la dirección del remitente, el asunto y el contenido del correo de inicio de sesión:
<?php
return [
'x-security' => [
'auth' => [
'emailSender' => 'no-reply@mysite.com',
'emailSubject' => 'Your login link',
'emailText' => "Hi,\n\nHere is your login link:\n%s\n\nIt is valid for 1 hour.\n\nBest regards"
]
]
];
El marcador %s en el texto se sustituye por la URL de inicio de sesión.
Registro automático
Por defecto, solo pueden iniciar sesión los usuarios ya presentes en la base de datos. Para registrar automáticamente a los nuevos usuarios:
<?php
return [
'x-security' => [
'auth' => [
'registration' => true
]
]
];
Redirección tras el inicio de sesión
Por defecto, el usuario es redirigido a la página de inicio tras iniciar sesión. Puedes cambiar esta URL:
<?php
return [
'x-security' => [
'auth' => [
'redirection' => '/my-account'
]
]
];
Ten en cuenta que si el atributo Auth ha guardado la URL solicitada (parámetro storeUrl), esta se usará en su lugar.
Personalización de los nombres de tablas y campos
Si tus tablas o campos tienen nombres distintos de los nombres por defecto, puedes redefinirlos:
<?php
return [
'x-security' => [
'auth' => [
'userData' => [
'base' => 'auth_db',
'table' => 'tUser',
'id' => 'user_id',
'email' => 'user_mail',
'roles' => 'user_roles',
'services' => 'user_services',
],
'tokenData' => [
'base' => 'auth_db',
'table' => 'tToken',
'token' => 'token_hash',
'expiration' => 'expiration_date',
'user_id' => 'fk_user_id',
]
]
]
];
Para más detalles sobre todas las opciones de configuración, consulta la documentación completa del controlador/plugin Auth y del atributo Auth.