コンテンツにスキップ
Nibiru docsv0.9.2

認証

セッションベースの認証、事前構築されたログインフォーム、およびプロダクションからのユーザープラグインパターン。

Stable Reading time ~ 2 min Edit on GitHub

Nibiru はセッションベースの認証コア (Nibiru\Auth) と users モジュールを搭載しており、3つのコマンドで動作するログインフォーム、権限チェック、データベーススキーマを提供します。

Auth::auth($login, $password) は最下層の呼び出しです。このメソッドは:

  1. user_login でユーザーを検索します。
  2. [SECURITY] password_hash から取得したソルトを使用して、保存されたパスワードを復号化します。
  3. マッチする場合、$_SESSION['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.']);
}

CLIで一度生成します:

Terminal window
./nibiru -m users

その後、対応するマイグレーションを実行します。

Terminal window
./nibiru -mi local

あなたには以下があります:

  • application/module/users/ — モジュールフォルダ。
  • users テーブル — 005-user.sql によって作成されました。
  • User プラグイン — Nibiru\Module\Users\Plugin\UserisAuthorized(), 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"loginForm() 内で設定されているため、フォームは正しいアクションに投稿されます。

単純なチェック:

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

ロール認識のチェックのために、デモンストレーションアプリケーションは同じモジュールから Acl プラグインを使用しています。

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

テーブル acl, user_to_acl, acl-data(マイグレーション 001, 008, 011)が役割/権限の基盤を形成しています。

本番環境では、Auth::auth() を強化されたバージョンに置き換えてください。

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

既存の行を最初のログイン時に password_needs_rehash で移行します。

NibiruはCSRFトークンを生成しません。自分で追加してください:

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

フォームに <input type="hidden" name="csrf" value="{$csrf}"> を埋め込んでください。

QRコードログイン(TPMSパターン)

Section titled “QRコードログイン(TPMSパターン)”

生産アプリケーションには、QRコードベースのマジックリンクログインが含まれています。このフローは以下の通りです:

  1. ユーザーがQRコードをスキャン → URLは /login/token/<one-time-token> です。
  2. tokenAction はトークンを検証し、セッションを作成します。

フレームワークはすでに bacon/bacon-qr-codepicqer/php-barcode-generator を Composer 経由で依存しているため、QR コードをインラインでレンダリングできます:

$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 は置き換えられ、マージされません。デフォルトの Auth::auth() によって。ログイン前に保存したものは失われます。必要に応じて明示的に保存と復元してください。
  • レート制限はありません。Fail2Ban またはミドルウェアスタイルの観察者を追加してください。
  • セッションクッキーにはフラグが必要です。php.ini の session.cookie_secure = 1session.cookie_httponly = 1、および session.cookie_samesite = "Lax" を本番環境で設定してください。