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

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.

## Le noyau d'authentification

`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' => …]]`.
```php
$auth = new \Nibiru\Auth();
if ($auth->auth($_POST['login'], $_POST['password'])) {
    View::forwardTo('/dashboard');
} else {
    View::assign(['error' => 'Invalid credentials.']);
}
```
:::caution
Le schéma par défaut stocke les mots de passe avec **AES_DECRYPT** cléfée par le sel INI — réversible. Suffisante pour la prototypage, **pas** pour la production publique. Remplacez-le par `password_hash()` / `password_verify()` pour les applications réelles. Consultez *Amélioration*, ci-dessous.
:::

## Le module Utilisateurs

Générer une fois avec la CLI :
```bash
./nibiru -m users
```
Exécutez ensuite les migrations correspondantes :
```bash
./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()`.

## Un flux de connexion complet

`application/controller/loginController.php`:
```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`:
```smarty
{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.

## Protection des contrôleurs

Le test simple :
```php
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 :
```php
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.

## Renforcement

Pour la production, remplacez `Auth::auth()` par une version renforcée :
```php
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`.

## CSRF

Nibiru ne génère pas les jetons CSRF pour vous. Ajoutez-les vous-même :
```php
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.

## Connexion par code QR (modèle TPMS)

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 :
```php
$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]);
```
## Tracas courantes

- **`$_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.
