Zum Inhalt springen
Nibiru docsv0.9.2

Auth

Sitzungs-basierte Authentifizierung, das vordefinierte Anmeldeformular und das Benutzer-Plugin-Muster aus der Produktion.

Stable Reading time ~ 3 min Edit on GitHub

Nibiru bietet einen auf Sitzungen basierenden Authentifizierungskern (Nibiru\Auth) und ein Modul users, das Ihnen eine funktionierende Anmeldeformular, eine Autorisierung überprüft und das Datenbankschema in drei Befehlen bereitstellt.

Auth::auth($login, $password) ist der niedrigste Funktionsaufruf. Er:

  1. Sucht den Benutzer nach user_login.
  2. Entschlüsselt das gespeicherte Passwort mit dem Salt aus [SECURITY] password_hash.
  3. Bei Übereinstimmung wird $_SESSION durch ['auth' => ['session_id' => …, 'user_id' => …, 'login' => …]] ersetzt.
$auth = new \Nibiru\Auth();
if ($auth->auth($_POST['login'], $_POST['password'])) {
View::forwardTo('/dashboard');
} else {
View::assign(['error' => 'Invalid credentials.']);
}

Generieren Sie es einmal mit der Befehlszeilenschnittstelle (CLI):

Terminal-Fenster
./nibiru -m users

Führen Sie dann die entsprechenden Migrationen aus:

Terminal-Fenster
./nibiru -mi local

Sie haben jetzt:

  • application/module/users/ — der Modulordner.
  • users Tabelle — erstellt durch 005-user.sql.
  • User Plugin — Nibiru\Module\Users\Plugin\User mit 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" wird in loginForm() gesetzt, sodass das Formular an die richtige Aktion gepostet wird.

Die einfache Überprüfung:

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

Für rollebasierte Überprüfungen verwenden die Showcase-Anwendungen den Acl-Plugin aus dem gleichen Modul:

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

Tabellen acl, user_to_acl, acl-data (Migrationen 001, 008, 011) bilden die Grundlage für Rollen und Berechtigungen.

Für die Produktion ersetzen Sie Auth::auth() durch eine verstärkte Version:

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

Migrieren Sie vorhandene Zeilen beim ersten Anmelden mit password_needs_rehash.

Nibiru generiert keine CSRF-Token für Sie. Fügen Sie diese selbst hinzu:

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

Fügen Sie <input type="hidden" name="csrf" value="{$csrf}"> in Ihr Formular ein.

Produktionsanwendungen umfassen eine QR-codes-basierte Magics-Link-Anmeldung, die kurzlebige Token ausgibt. Der Ablauf:

  1. Der Benutzer scannen einen QR-Code → Die URL lautet /login/token/<one-time-token>.
  2. tokenAction validiert und erstellt eine Sitzung.

Das Framework hängt bereits von bacon/bacon-qr-code und picqer/php-barcode-generator über Composer ab, sodass Sie QR-Codes direkt rendern können:

$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 wird nicht zusammengeführt, sondern ersetzt durch die Standardmethode Auth::auth(). Alles, was Sie vor dem Login gespeichert hatten, geht verloren. Speichern und wiederherstellen Sie explizit, wenn dies erforderlich ist.
  • Keine Rate Limiting. Fügen Sie Fail2Ban oder einen middleware-artigen Beobachter hinzu.
  • Sitzungs-Cookies benötigen Flags. Setzen Sie session.cookie_secure = 1, session.cookie_httponly = 1 und session.cookie_samesite = "Lax" in der php.ini für die Produktion.