nette/security
Nette Security provides authentication and authorization for PHP apps, with ready-to-use user identity, login/logout handling, roles and permissions, and easy integration with Nette Framework services for secure access control.
Installation:
composer require nette/security
Note: Requires PHP 8.1+. Use nette/utils as a dependency if not already present.
Basic Configuration (via Laravel Service Provider):
use Nette\Security\SecurityExtension;
use Nette\DI\Container;
public function register()
{
$this->container->addService('security', function (Container $container) {
$security = new SecurityExtension();
$security->setup($container);
return $security;
});
}
First Use Case: User Authentication
use Nette\Security\User;
use Nette\Security\Identity;
// In a controller/middleware
$user = app('security.user');
if ($user->isLoggedIn()) {
$identity = $user->getIdentity();
// $identity is an Identity object with roles/data
}
Quick Login Flow:
$authenticator = app('security.authenticator');
$authenticator->authenticate([
'username' => 'admin',
'password' => 'secure123'
]);
Login:
$authenticator = app('security.authenticator');
$authenticator->authenticate([
'username' => $request->input('username'),
'password' => $request->input('password')
]);
Tip: Use SimpleAuthenticator for basic setups (supports dynamic passwords via $userData).
Logout:
$user = app('security.user');
$user->logout(true); // true = clear identity immediately
if ($user->isAllowed('admin', 'dashboard')) {
// Grant access
}
Pattern: Use Authorizator for resource-level permissions:
$authorizator = app('security.authorizator');
if ($authorizator->isAllowed($user->getIdentity(), 'resource:post', 'edit')) {
// Allow edit
}
$identityHandler = app('security.identityHandler');
$guestIdentity = $identityHandler->getGuestIdentity(); // Returns ?Identity
Use Case: Assign guest roles (e.g., ['guest']) without hitting storage.$storage = app('security.sessionStorage');
$storage->setExpiration(new \DateTime('+1 hour')); // No silent revalidation
Tip: Use User::setExpiration() for user-specific timeouts:
$user->setExpiration(new \DateTime('+30 minutes'), true);
Middleware Integration:
namespace App\Http\Middleware;
use Nette\Security\User;
class Authenticate
{
public function handle($request, Closure $next, User $user)
{
if (!$user->isLoggedIn()) {
return redirect()->route('login');
}
return $next($request);
}
}
Service Provider Binding:
public function boot()
{
$this->app->singleton('security.user', function ($app) {
return new User(
$app['security.authenticator'],
$app['security.authorizator'],
$app['security.sessionStorage']
);
});
}
Password Hashing:
$passwords = app('security.passwords');
$hash = $passwords->hash('plaintext', 'bcrypt', ['cost' => 12]);
$isValid = $passwords->verify('plaintext', $hash);
Mock Authenticator:
$mockAuthenticator = $this->createMock(Authenticator::class);
$mockAuthenticator->method('authenticate')->willReturn(new Identity('user1', ['user']));
$this->app->instance('security.authenticator', $mockAuthenticator);
Assert Roles:
$this->assertTrue($user->isInRole('admin'));
$this->assertFalse($user->isInRole('guest'));
Session Storage Quirks:
SameSite attribute setup. Use:
$storage = new CookieStorage('auth', [
'httponly' => true,
'secure' => env('APP_ENV') === 'production',
'samesite' => 'Lax',
]);
BC Breaks:
IUserStorage is removed. Migrate to UserStorage.IAuthorizator → Authorizator (no I prefix).Guest Identity:
getGuestIdentity() if isLoggedIn() is false. Override IdentityHandler to customize:
class CustomIdentityHandler implements IdentityHandler
{
public function getGuestIdentity(): ?Identity
{
return new Identity('guest', ['guest'], ['name' => 'Anonymous']);
}
}
Password Hashing:
Passwords::hash() throws on empty input (v3.0.5+).Identity Cache:
$user->refreshStorage() if roles/data appear stale.Authorization Debugging:
Authorizator decisions:
$authorizator->onDenied[] = function ($resource, $role, $privilege) {
\Log::debug("Access denied: {$resource} for {$role}");
};
Session Issues:
SessionStorage::getState() for null (expired) vs. Identity objects.UserPanel for debugging (requires Tracy):
$userPanel = new UserPanel($user);
echo $userPanel->render();
Custom Authenticators:
class DatabaseAuthenticator implements Authenticator
{
public function authenticate(array $credentials): ?Identity
{
$user = User::where('email', $credentials['username'])->first();
if ($user && $this->passwords->verify($credentials['password'], $user->password)) {
return new Identity($user->id, ['user', ...$user->roles]);
}
return null;
}
}
Dynamic Roles:
Identity to fetch roles from a database:
class DynamicIdentity extends SimpleIdentity
{
public function getRoles(): array
{
return $this->roles ?? $this->loadRolesFromDb($this->id);
}
}
Event Listeners:
User::onLogin/onLogout:
$user->onLogin[] = function ($identity) {
\Log::info("User {$identity->id} logged in");
};
Session Driver Conflicts:
database or redis sessions, ensure SessionStorage is initialized after Laravel’s session starts:
$storage = new SessionStorage($session->getHandler());
CSRF Integration:
nette/security does not handle CSRF. Use Laravel’s built-in middleware alongside:
$middleware->append(\App\Http\Middleware\EncryptCookies::class);
$middleware->append(\Illuminate\Session\Middleware\StartSession::class);
Password Reset:
Password::reset() and use nette/security for hashing:
$newHash = app('security.passwords')->hash($request->password);
User::where('email', $email)->update(['password' => $newHash]);
How can I help you explore Laravel packages today?