Ir al contenido
Nibiru docsv0.9.2

Auth

Autenticación basada en sesiones, el formulario de inicio de sesión preconstruido y el patrón del complemento de usuario desde la producción.

Stable Reading time ~ 3 min Edit on GitHub

Nibiru incluye un núcleo de autenticación basado en sesiones (Nibiru\Auth) y un módulo users que te proporciona un formulario de inicio de sesión funcional, una verificación de autorización y el esquema de base de datos en tres comandos.

Auth::auth($login, $password) es la llamada de nivel más bajo. Realiza lo siguiente:

  1. Busca al usuario por user_login.
  2. Desencripta la contraseña almacenada utilizando el sal de [SECURITY] password_hash.
  3. Si coincide, reemplaza $_SESSION con ['auth' => ['session_id' => …, 'user_id' => …, 'login' => …]].
$auth = new \Nibiru\Auth();
if ($auth->auth($_POST['login'], $_POST['password'])) {
View::forwardTo('/dashboard');
} else {
View::assign(['error' => 'Invalid credentials.']);
}

Genera una vez con la CLI:

Ventana de terminal
./nibiru -m users

Luego ejecuta las migraciones correspondientes:

Ventana de terminal
./nibiru -mi local

Ahora tienes:

  • application/module/users/ — la carpeta del módulo.
  • users tabla — creada por 005-user.sql.
  • User plugin — Nibiru\Module\Users\Plugin\User con isAuthorized(), loginForm(), currentUser().

application/controller/loginController.php:

<?php
namespace Nibiru;
use Nibiru\Adapter\Controller;
use Nibiru\Module\Users\Plugin\User;
class loginController extends Controller
{
private User $user;
public function __construct() {
parent::__construct();
$this->user = new User();
}
public function pageAction() {
if ($this->user->isAuthorized()) {
View::forwardTo('/');
return;
}
View::assign([
'title' => 'Sign in',
'loginForm' => $this->user->loginForm(),
]);
}
public function submitAction() {
if ($_SERVER['REQUEST_METHOD'] !== 'POST') return;
$auth = new Auth();
if ($auth->auth($_POST['login'] ?? '', $_POST['password'] ?? '')) {
View::forwardTo('/');
} else {
View::assign(['error' => 'Invalid login.']);
}
}
public function logoutAction() {
unset($_SESSION['auth']);
session_regenerate_id(true);
View::forwardTo('/login');
}
public function navigationAction() {
JsonNavigation::getInstance()->loadJsonNavigationArray();
}
}

application/view/templates/login.tpl:

{include 'shared/header.tpl'}
<body>
{include file="navigation.tpl"}
<main class="container">
<h1>{$title}</h1>
{if $error}<div class="alert alert-danger">{$error}</div>{/if}
{$loginForm nofilter}
</main>
{include 'shared/footer.tpl'}
</body>

Form::action="/login/submit" está configurado dentro de loginForm() para que el formulario se envíe a la acción correcta.

La verificación sencilla:

public function pageAction() {
if (!$this->user->isAuthorized()) {
View::forwardTo('/login');
return;
}
/ ...
}

Para las verificaciones basadas en roles, las aplicaciones de demostración utilizan el complemento Acl del mismo módulo:

use Nibiru\Module\Users\Plugin\Acl;
if (!Acl::can('edit', 'documents')) {
View::forwardTo('/forbidden');
return;
}

Las tablas acl, user_to_acl, acl-data (migraciones 001, 008, 011) forman la base de roles y permisos.

Para producción, reemplace Auth::auth() con una versión más robusta:

namespace Nibiru;
use Nibiru\Pdo;
class HardenedAuth
{
public function auth(string $login, string $password): bool {
$row = Pdo::fetchRow(
'SELECT user_id, user_pass FROM "user" WHERE user_login = :l AND user_account_active = 1',
[':l' => $login]
);
if (!$row || !password_verify($password, $row['user_pass'])) {
return false;
}
if (password_needs_rehash($row['user_pass'], PASSWORD_ARGON2ID)) {
Pdo::update('user', [
'user_pass' => password_hash($password, PASSWORD_ARGON2ID),
], ['user_id' => $row['user_id']]);
}
session_regenerate_id(true);
$_SESSION['auth'] = [
'session_id' => session_id(),
'user_id' => $row['user_id'],
'login' => $login,
];
return true;
}
}

Migrar filas existentes en el primer inicio de sesión con password_needs_rehash.

Nibiru no genera tokens CSRF automáticamente. Añádelos tú mismo:

public function pageAction() {
if (!isset($_SESSION['csrf'])) {
$_SESSION['csrf'] = bin2hex(random_bytes(16));
}
View::assign(['csrf' => $_SESSION['csrf']]);
}
public function submitAction() {
if (!hash_equals($_SESSION['csrf'] ?? '', $_POST['csrf'] ?? '')) {
http_response_code(419);
return;
}
/ ...handle submission...
}

Incorpore <input type="hidden" name="csrf" value="{$csrf}"> en su formulario.

Inicio de sesión con código QR (patrón TPMS)

Sección titulada «Inicio de sesión con código QR (patrón TPMS)»

Las aplicaciones de producción incluyen un inicio de sesión basado en código QR con enlace mágico que emite tokens de corta duración. El flujo:

  1. El usuario escanea el código QR → La URL es /login/token/<one-time-token>.
  2. tokenAction valida y crea una sesión.

El marco ya depende de bacon/bacon-qr-code y picqer/php-barcode-generator a través de Composer, por lo que puedes representar códigos QR en línea:

$writer = new \BaconQrCode\Writer(new \BaconQrCode\Renderer\ImageRenderer(
new \BaconQrCode\Renderer\RendererStyle\RendererStyle(220),
new \BaconQrCode\Renderer\Image\SvgImageBackEnd()
));
$svg = $writer->writeString('https://app.example.com/login/token/' . $token);
View::assign(['qr' => $svg]);
  • $_SESSION es reemplazado, no fusionado por el predeterminado Auth::auth(). Cualquier cosa que hayas almacenado antes del inicio de sesión se pierde. Guarda y restaura explícitamente si es necesario.
  • No hay límite de velocidad. Agrega Fail2Ban o un observador estilo middleware.
  • Las cookies de sesión necesitan banderas. Establece session.cookie_secure = 1, session.cookie_httponly = 1 y session.cookie_samesite = "Lax" en php.ini para producción.