The Authentication component provides a simple, secure, and flexible service to manage user authentication in your application. It handles session management, secure Remember Me cookies, and Role-Based Access Control (RBAC) out of the box.
To activate authentication features in your application, controllers should inherit from \Krystal\Application\Controller\AbstractAuthAwareController instead of the default \Krystal\Application\Controller\AbstractController. You can access the authentication manager directly in your controller actions via $this->authManager.
When inheriting from AbstractAuthAwareController, implement the following protected methods to customize the authentication lifecycle:
getAuthService() — Must return the authentication service instance.onSuccess() — Invoked automatically once a user successfully provides valid credentials.onFailure() — Invoked automatically when an unauthenticated user attempts to access a protected controller action.onNoRights() — Invoked automatically when an authenticated user attempts to access an area without the required permissions.The framework needs to know how to fetch a user from your database. You provide this logic as a simple callable (a closure, a static method, or an invokable class) and register it with the AuthManager during your application's bootstrap phase.
The callable receives an identifier (either a string $login during login, or an int|string $userId during "Remember Me" validation) and must return an array with at least id, login, and password_hash. It can optionally include role and remember_token_version.
$userProvider = function ($identifier) use ($pdo) {
// Determine if searching by ID (Remember Me) or Login (Standard Login)
$column = is_numeric($identifier) ? 'id' : 'username';
$stmt = $pdo->prepare("SELECT id, username AS login, password_hash, role, remember_token_version FROM users WHERE {$column} = ?");
$stmt->execute([$identifier]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
return $user ?: null;
};
// Register it with the framework's auth manager
$this->authManager->setUserProvider($userProvider);
To authenticate a user, call login() with the submitted credentials and an optional Remember Me flag:
public function loginAction()
{
$login = $this->request->getPost('username');
$plainPassword = $this->request->getPost('password');
$rememberMe = (bool) $this->request->getPost('remember_me');
if ($this->authManager->login($login, $plainPassword, $rememberMe)) {
// Success! User is logged in.
return $this->response->redirect('/dashboard');
} else {
// Failure. Show invalid credentials error.
return $this->view->render('auth/login', ['error' => 'Invalid credentials']);
}
}
Note: If a password hash requires re-hashing due to algorithm updates, the framework detects this via password_needs_rehash().
public function dashboardAction()
{
if ($this->authManager->isLoggedIn()) {
$userId = $this->authManager->getId();
$userRole = $this->authManager->getRole();
$userLogin = $this->authManager->getLogin();
// Retrieve full user payload
$fullUser = $this->authManager->getUser();
return $this->render('dashboard', compact('fullUser'));
}
return $this->redirect('/login');
}
To sign a user out and clear their session, call logout():
public function logoutAction()
{
$this->authManager->logout();
return $this->response->redirect('/login');
}
Restrict access to specific controller actions or routes based on user roles.
Check if the current user possesses one of the allowed roles:
public function deletePostAction()
{
// Allow only 'admin' or 'moderator'
if (!$this->authManager->isAllowed(['admin', 'moderator'])) {
return $this->view->render('errors/403');
}
// Proceed with action
}
Attach permission definitions directly within route configurations using allow or disallow directives:
// Grant access exclusively to specific roles
'/admin/delete-post' => [
'controller' => 'Admin\PostController@delete',
'allow' => ['admin'],
],
// Deny access to specific roles (triggers onNoRights automatically)
'/admin/some-secured-action' => [
'controller' => 'SomeController@someAction',
'disallow' => ['reviewer'],
]
Increment the remember_token_version column in your database to invalidate all active "Remember Me" tokens across every device without altering the user's password:
public function changePasswordAction()
{
$userId = $this->authManager->getId();
// 1. Update password in database...
// 2. Invalidate all existing sessions/tokens across devices
$stmt = $this->pdo->prepare("UPDATE users SET remember_token_version = remember_token_version + 1 WHERE id = ?");
$stmt->execute([$userId]);
return $this->redirect('/dashboard');
}
hash_equals() and strict comparisons (===) across hash checks.password_hash() and password_verify().remember_token_version changes.id, login, role).