Aller au contenu
Nibiru docsv0.9.2

Auth

L'authentification basée sur les sessions, le formulaire de connexion préconstruit et le modèle de plugin Utilisateur provenant de la production.

Stable Reading time ~ 3 min Edit on GitHub

Nibiru embarque un noyau d’authentification basé sur les sessions (Nibiru\Auth) et un module utilisateurs qui vous offre un formulaire de connexion fonctionnel, une vérification d’autorisation et le schéma de base de données en trois commandes.

Auth::auth($login, $password) est l’appel de niveau le plus bas. Il :

  1. Recherche l’utilisateur par user_login.
  2. Décrypte le mot de passe stocké en utilisant le sel de [SECURITY] password_hash.
  3. En cas de correspondance, remplace $_SESSION par ['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.']);
}

Générer une fois avec la CLI :

Fenêtre de terminal
./nibiru -m users

Exécutez ensuite les migrations correspondantes :

Fenêtre de terminal
./nibiru -mi local

Vous avez maintenant :

  • application/module/users/ — le dossier du module.
  • users table — créée par 005-user.sql.
  • User plugin — Nibiru\Module\Users\Plugin\User avec 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 défini dans loginForm() afin que le formulaire soumette à l’action correcte.

Le test simple :

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

Pour les vérifications basées sur le rôle, les applications de démonstration utilisent le plugin Acl du même module :

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

Les tables acl, user_to_acl, acl-data (migrations 001, 008, 011) forment la base rôle/permission.

Pour la production, remplacez Auth::auth() par une version renforcée :

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;
}
}

Migrer les lignes existantes lors du premier connexion avec password_needs_rehash.

Nibiru ne génère pas les jetons CSRF pour vous. Ajoutez-les vous-même :

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...
}

Intégrez <input type="hidden" name="csrf" value="{$csrf}"> dans votre formulaire.

Les applications de production incluent une connexion par lien magique basée sur un code QR qui émet des jetons à durée limitée. Le flux :

  1. L’utilisateur scanne le QR code → l’URL est /login/token/<jeton-unique>.
  2. tokenAction valide et crée une session.

Le framework dépend déjà de bacon/bacon-qr-code et picqer/php-barcode-generator via Composer, donc vous pouvez afficher des codes QR en ligne :

$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 est remplacé, pas fusionné par le Auth::auth() par défaut. Tout ce que vous aviez stocké avant la connexion est perdu. Sauvegardez et restaurez explicitement si nécessaire.
  • Aucune limitation de taux. Ajoutez Fail2Ban ou un observateur de type middleware.
  • Les cookies de session ont besoin de drapeaux. Définissez session.cookie_secure = 1, session.cookie_httponly = 1 et session.cookie_samesite = "Lax" dans le php.ini pour la production.