Helper del controlador Auth


1Presentación

Este helper se usa para gestionar la autenticación de los usuarios de un sitio web. Es a la vez el controlador que ofrece la interfaz de autenticación y el plugin que transmite la información de autenticación a otros plugins/controladores.

El sistema de autenticación propuesto no usa contraseña: el usuario introduce su dirección de correo electrónico, y si está registrada en la base de datos, se envía un mensaje a esa dirección con un enlace. Al hacer clic en el enlace, el usuario vuelve al sitio ya autenticado. El enlace solo puede usarse una vez y tiene una vida útil limitada a una hora.

Por defecto, el controlador solo acepta la conexión de usuarios que ya han sido añadidos previamente a la base de datos. Pero se puede configurar para que los nuevos usuarios se registren automáticamente cuando introducen su dirección de correo electrónico por primera vez.


2Base de datos

Este helper necesita dos tablas en la base de datos, una llamada User (que contiene la información de los usuarios) y otra llamada AuthToken (que contiene los tokens de conexión enviados por correo electrónico).

La tabla User debe contener los siguientes campos:

  • id (int): Clave primaria.
  • date_creation (datetime): Fecha de creación del usuario.
  • date_last_login (datetime): Fecha de la última autenticación del usuario.
  • date_last_access (datetime): Fecha del último acceso del usuario.
  • email (string): Dirección de correo electrónico del usuario.
  • name (string): Nombre del usuario (campo obligatorio, incluso si no lo usas).
  • roles (set): Roles asignados al usuario.
  • services (set): Servicios a los que el usuario tiene acceso.

La tabla AuthToken debe contener los siguientes campos:

  • token (string): Hash (algoritmo SHA-256) del token de conexión.
  • expiration (datetime): Fecha y hora de expiración del token.
  • user_id (int): Clave foránea hacia el usuario.

Aquí tienes un ejemplo de consulta para crear estas tablas, en la que debes personalizar los campos roles y services de la tabla User:

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;

3Configuración del plugin y del controlador

Debes añadir el objeto \Temma\Controllers\Auth como pre-plugin en la configuración de Temma, y crear una ruta para hacer accesible el controlador:

<?php

return [
    'plugins' => [
        '_pre' => [
            '\Temma\Controllers\Auth'
        ]
    ],
    'routes' => [
        'auth' => '\Temma\Controllers\Auth'
    ]
];

4Configuración de los mensajes de conexión

Estos son los parámetros por defecto utilizados para enviar los mensajes de conexión:

  • Dirección de correo electrónico del remitente: contact@ + nombre de dominio de tu sitio
  • Título del mensaje: Your connection link
  • Texto del mensaje:
    Hi,
    
    Here is your connection link:
    %s
    
    It is valid for 1 hour and can only be used once.
    
    Best regards

Estos parámetros se pueden modificar:

<?php

return [
    'x-security' => [
        'auth' => [
            'emailSender'  => 'no-reply@mycompany.com',
            'emailSubject' => 'Aquí tienes tu enlace de conexión',
            'emailText'    => 'Haz clic en este enlace para conectarte: %s',
        ]
    ]
];
  • Línea 6: Dirección de correo electrónico del remitente del mensaje.
  • Línea 7: Título del mensaje de conexión.
  • Línea 8: Contenido del mensaje, en texto plano. El marcador %s se sustituye por la URL de conexión.

El controlador utiliza el objeto \Temma\Utils\Email para enviar los mensajes de conexión. Por lo tanto, es posible usar la configuración de ese objeto para desactivar los envíos, autorizar el envío solo a determinados dominios, o añadir destinatarios en copia o en copia oculta.


5Configuración del registro de usuarios

Para registrar automáticamente a los usuarios que no están en la base de datos, basta con añadir esta configuración:

<?php

return [
    'x-security' => [
        'auth' => [
            'registration' => true
        ]
    ]
];

6Configuración de las redirecciones

Por defecto, una vez que un usuario está autenticado, es redirigido a la página de inicio del sitio. Es posible configurar una URL diferente:

<?php

return [
    'x-security' => [
        'auth' => [
            'redirection' => '/myAccount'
        ]
    ]
];

Ten en cuenta que si existe una variable de sesión llamada authRequestedUrl, su contenido se usará para realizar la redirección (y la variable se elimina de la sesión). Esta variable puede definirse mediante el atributo Auth, si su parámetro storeUrl está definido como true.


7Configuración de la base de datos

Por defecto, los nombres de las tablas y de los campos son los indicados anteriormente, y estas tablas deben ubicarse en la base de datos abierta por la conexión llamada db en la directiva de configuración dataSources. Tienes la posibilidad de indicar el nombre de la base de datos, los nombres de las tablas y los nombres de los campos, si son diferentes de los valores por defecto:

<?php

return [
    'x-security' => [
        'auth' => [
            'userData' => [
                'base'     => 'auth_app',
                'table'    => 'tUser',
                'id'       => 'user_id',
                'email'    => 'user_mail',
                'roles'    => 'user_roles',
                'services' => 'user_services',
            ],
            'tokenData' => [
                'base'       => 'auth_app',
                'table'      => 'tToken',
                'token'      => 'token_string',
                'expiration' => 'expiration_date',
                'user_id'    => 'identifier_user',
            ]
        ]
    ]
];
  • Líneas 6 a 13: Configuración de la tabla de usuarios.
    • Línea 7: Nombre de la base de datos que contiene la tabla.
    • Línea 8: Nombre de la tabla.
    • Línea 9: Nombre del campo que contiene la clave primaria.
    • Línea 10: Nombre del campo que contiene la dirección de correo electrónico del usuario.
    • Línea 11: Nombre del campo que contiene los roles del usuario.
    • Línea 12: Nombre del campo que contiene los servicios a los que el usuario tiene acceso.
  • Líneas 14 a 20: Configuración de la tabla de tokens de conexión.
    • Línea 15: Nombre de la base de datos que contiene la tabla.
    • Línea 16: Nombre de la tabla.
    • Línea 17: Nombre del campo que contiene el token.
    • Línea 18: Nombre del campo que contiene la fecha de expiración del token.
    • Línea 19: Nombre del campo que contiene el identificador del usuario.

En este ejemplo, user_id, user_mail, user_roles y user_services son los nombres reales de los campos de la tabla tUser, que contiene los usuarios.
Y token_string, expiration_date e identifier_user son los nombres reales de los campos de la tabla tToken, que contiene los tokens de autenticación.

Si la tabla de usuarios contiene otros campos que quieres recuperar, puedes añadirlos a la lista. Si los campos no deben renombrarse, pon el mismo nombre en la clave y en el valor:

<?php

return [
    'x-security' => [
        'auth' => [
            'userData' => [
                'email'        => 'user_mail',
                'name'         => 'user_name',
                'organization' => 'organization',
                'birthday'     => 'birthday',
            ]
        ]
    ]
];
  • Línea 7: Nombre del campo que contiene la dirección de correo electrónico del usuario.
  • Línea 8: Campo user_name añadido, renombrado a name.
  • Línea 9: Campo organization añadido, sin renombrar.
  • Línea 10: Campo birthday añadido, sin renombrar.

8Configuración de la base de datos mediante DAO

En lugar de usar el archivo de configuración (etc/temma.php) para definir los parámetros de la base de datos, es posible usar DAO personalizadas.

Ejemplo de configuración:

<?php

return [
    'x-security' => [
        'auth' => [
            'userDao'  => '\MyApp\UserDao',
            'tokenDao' => '\MyApp\TokenDao',
        ]
    ]
];

Ejemplo de DAO:

namespace MyApp;

class UserDao extends \Temma\Dao\Dao {
    protected $_tableName = 'tUser';
    protected $_idField = 'user_id';
    protected $_fields = [
        'user_id'       => 'id',
        'user_mail'     => 'email',
        'user_roles'    => 'roles',
        'user_services' => 'services',
        'user_name'     => 'name',
        'organization',
        'birthday',
    ];
}

9URLs del controlador

El controlador ofrece las siguientes URLs:

  • /auth/login: Página que muestra el formulario de autenticación.
  • /auth/logout: URL a la que enviar al usuario para cerrar sesión.

La URL /auth redirige a /auth/login.


10Template

El controlador utiliza un template Smarty situado en templates/auth/login.tpl. La versión proporcionada por Temma es mínima, y te recomendamos que la uses como base para crear tu propia versión.


11Estado de inicio de sesión y protección anti-robot

Después de cada intento de envío del formulario de inicio de sesión, se pone a disposición de la plantilla templates/auth/login.tpl una variable flash $__authStatus. Valores posibles:

  • logout: el usuario acaba de cerrar sesión.
  • email: la dirección de correo enviada no es válida.
  • attempts: se han realizado demasiados intentos de autenticación desde esta sesión en la última hora.
  • robot: el control anti-robot ha fallado (ver más abajo).
  • tokenSent: se ha enviado correctamente un enlace de inicio de sesión por correo.
  • badToken: el token de conexión no es válido o ha expirado.

Por defecto, el envío del formulario de inicio de sesión también requiere un campo hash, calculado mediante JavaScript a partir del tiempo transcurrido entre la carga de la página y el envío del formulario, para filtrar robots. En su ausencia (o si es incorrecto, demasiado antiguo o demasiado reciente), la variable $__authStatus toma el valor 'robot'.

Para implementarlo, genera un campo oculto hash cuyo valor sea la cadena "$timeDiff#$loginTime#$hash", donde $loginTime es la marca de tiempo (en milisegundos) de la carga de la página, $timeDiff el número de milisegundos transcurridos desde entonces, y $hash el hash MD5 de la cadena "$timeDiff:$loginTime:$email:$userAgent" (con la dirección de correo enviada y la cadena user-agent del navegador).

Este mecanismo puede desactivarse por completo (para pruebas, o si implementas tu propia protección anti-robot):

<?php

return [
    'x-security' => [
        'auth' => [
            'robotCheckDisabled' => true
        ]
    ]
];

12Variables de template

El plugin define las siguientes variables de template:

  • currentUserId: Identificador del usuario actualmente autenticado.
  • currentUser: Datos del usuario actual. Es un array asociativo que contiene las siguientes claves:
    • id: Identificador del usuario.
    • date_creation: Fecha de creación del usuario.
    • date_last_login: Fecha de la última autenticación del usuario.
    • date_last_access: Fecha del último acceso del usuario.
    • email: Dirección de correo electrónico del usuario.
    • name: Nombre del usuario.
    • roles: Array asociativo cuyas claves son los roles del usuario (asociadas al valor true).
    • services: Array asociativo cuyas claves son los servicios a los que el usuario tiene acceso (asociadas al valor true).

Estas variables pueden usarse en otros plugins, controladores y templates.


13Atributo Auth

Temma ofrece el atributo Auth, que permite especificar si un controlador o una acción solo puede ser accedido por un usuario autenticado (o no). Como este atributo se basa en la presencia de una variable de template currentUser (así como en sus claves roles y services), es totalmente compatible con este helper.

Por ejemplo, puedes especificar que un controlador solo sea accesible para usuarios conectados:

use \Temma\Attributes\Auth as TµAuth;

#[TµAuth]
class Account extends \Temma\Web\Controller {
    // ...
}

También es posible restringir el acceso a controladores o acciones a usuarios con un rol específico o con acceso a un servicio concreto:

use \Temma\Attributes\Auth as TµAuth;

class MyController extends \Temma\Web\Controller {
    // acceso permitido únicamente a usuarios con
    // el rol "manager"
    #[TµAuth('manager')]
    public function action1() { }

    // permitido únicamente a usuarios con acceso
    // a los servicios "images" o "text"
    #[TµAuth(service: ['images', 'text'])]
    public function action2() { }
}

Consulta la documentación del atributo para ver todas sus posibilidades.