Search by

nawasara / auth-primitives

pringgojsnawasara

Low-level auth primitives for Nawasara packages: sudo mode (session window, Livewire attribute, route middleware), built to live below the application shell so domain packages can depend on it without dragging in core.

Package info

github.com/nawasara/auth-primitives

pkg:composer/nawasara/auth-primitives

Statistics

Installs: 366

Dependents: 21

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.2 2026-10-15 02:49 UTC

This package is auto-updated.

Last update: 2026-10-15 02:49:55 UTC


README

Low-level auth primitives for Nawasara packages. It lives below the application shell (nawasara/core) so any domain package can depend on it without pulling in the rest of Nawasara.

What's in the box

Primitive Purpose
Nawasara\AuthPrimitives\Auth\Sudo Session window: the single source of truth for whether the user recently re-authenticated
Nawasara\AuthPrimitives\Http\Middleware\EnsureSudo Route gate, registered as the sudo middleware alias
#[Nawasara\AuthPrimitives\Attributes\RequiresSudo] Livewire method attribute that gates one action behind sudo
Nawasara\AuthPrimitives\Traits\WithSudo Livewire component trait that handles the step-up redirect
Nawasara\AuthPrimitives\Exceptions\SudoRequiredException Renderable exception (403 or redirect)
sudo_active(), sudo_remaining_seconds() Blade helpers

What's NOT in here

The OTP step-up itself (IdP redirect, callback, ID-token verification) is not in this package. It lives in nawasara/core's SudoController, which calls Sudo::confirm($userId) on a verified step-up. This split lets domain packages enforce a sudo window without depending on the integration plumbing.

Usage

Route-level

Route::get('db/drop/{name}', ...)->middleware(['auth', 'sudo']);

The sudo alias is registered automatically by AuthPrimitivesServiceProvider.

Livewire action-level

use Livewire\Component;
use Nawasara\AuthPrimitives\Attributes\RequiresSudo;
use Nawasara\AuthPrimitives\Traits\WithSudo;

class DangerousThings extends Component
{
    use WithSudo;

    #[RequiresSudo(reason: 'menghapus database')]
    public function dropDatabase(string $name): void
    {
        // …only runs inside an active sudo window
    }
}

Event listeners: the attribute does NOT run

#[RequiresSudo] is evaluated only when the method is called directly (wire:click="dropDatabase", $wire.dropDatabase()). When the same method runs as an event listener (#[On('...')], $listeners, Livewire.dispatch(...), $wire.dispatch(...)), Livewire calls __dispatch instead, the attribute hook filters by method name, and the action runs without sudo and without any error.

This was finding T6 of the security audit of 15 October 2026: deleting a User or Role through the confirm-delete modal never asked for sudo.

For any method reachable through an event, use the imperative guard from WithSudo inside the method. It works on both paths:

use Livewire\Attributes\On;
use Nawasara\AuthPrimitives\Traits\WithSudo;

class Table extends Component
{
    use WithSudo;

    #[On('confirm-delete')]
    public function delete(int $id): void
    {
        Gate::authorize('x.delete');

        if (! $this->requireSudo('menghapus x')) {
            return; // step-up redirect already issued
        }

        // ...only runs inside an active sudo window
    }
}

Keeping #[RequiresSudo] next to it is harmless, but it is not the guard. Quick check before shipping:

grep -rn -B3 "#\[RequiresSudo" --include=*.php packages | grep "#\[On"

Every hit must also call requireSudo() inside the method body.

Blade display

@if (sudo_active())
    <button wire:click="dropDatabase">Hapus</button>
@else
    <button wire:click="$dispatch('sudo-required')">Hapus (butuh konfirmasi)</button>
@endif

Config

Defaults are bundled. Publish to override:

php artisan vendor:publish --tag=auth-primitives:config
// config/auth-primitives.php
return [
    'sudo' => [
        'window_minutes' => env('NAWASARA_SUDO_WINDOW_MINUTES', 15),
        'acr' => env('NAWASARA_SUDO_ACR', 'sudo'),
    ],
];

License

MIT.