Authentication
Auth
is a service with a mutable state. The login and logout methods change the current user.
use Jasny\Auth\LoginException;
try {
$auth->login($_POST['username'], $_POST['password']);
} catch (LoginException $exception) {
http_response_code(400);
echo $exception->getMessage();
}
http_response_code(303);
header("Location: /dashboard");
echo "You're being redirected to <a href='/dashboard'>the dashboard</a>."
Methods
login
Auth::login(string $username, string $password)
Login with username and password.
Triggers a login event, which may be used to cancel the login.
The method will throw a LoginException
if login failed. The code will either be LoginException::INVALID_CREDENTIALS
or LoginException::CANCELLED
(if cancelled via the login event).
loginAs
Auth::loginAs(UserInterface $user)
Set user without verification.
Triggers a login event, which may be used to cancel the login. The method will throw a LoginException
if the login is cancelled.
logout
Auth::logout()
Clear the current user and context.
Triggers a logout event.
user
Auth::user(): UserInterface
Get the current user.
Use isLoggedIn()
to see if there is a logged in user. This function throws an AuthException
if no user is logged in.
time
Auth::time(): \DateTimeInterface
Get the login timestamp.
setContext
Auth::setContext(ContextInterface $context)
Set the current authorization context for the user.
context
Auth::context(): ContextInterface|null
Get the current context. Returns null
if the global context is used.
Events
Calling login
, loginAs
and logout
will trigger an event. To capture these event, register a PSR-14 event dispatcher.
use Jasny\Auth\Auth;
use Jasny\Auth\Authz;
use Jasny\Auth\Event;
use Jasny\EventDispatcher\EventDispatcher;
use Jasny\EventDispatcher\ListenerProvider;
$listeners = (new ListenerProvider())
->withListener(function(Event\Login $login): void {
if ($login->user()->isSuspended()) {
$login->cancel("Sorry, you're account is suspended'");
}
})
->withListener(function(Event\Login $login): void {
// do something
})
->withListener(function(Event\Logout $logout): void {
// do something
});
$levels = new Authz\Levels(['user' => 1, 'moderator' => 10, 'admin' => 100]);
$auth = (new Auth($levels, new AuthStorage()))
->withEventDispatcher(new EventDispatcher($listeners));
Session fixation
In a session fixation attack, an attacker gets hold of user’s session id and keeps using it. In order to mitigate such an attack, the session id should be regenerated on login and the session should be destroyed on logout.
$listeners = (new ListenerProvider())
->withListener(function(Event\Login $login): void {
session_regenerate_id();
})
->withListener(function(Event\Logout $logout): void {
session_destroy();
});
Recalc
Recalculate the authz roles and store the current auth information in the session.
Auth::recalc()
typically doesn’t have to be called explicitly. If the current user modifies his/her password (causing an auth checksum mismatch), this needs to be called to prevent the current user from being logged out.
$auth->user()->changePassword($_GET['new_password']);
$auth->recalc();
If the role of the current user is changed, this also needs to be called to use the modified role for authorization.
$auth->user()->setRole('admin');
$auth->is('admin'); // still returns false
$auth->recalc();
$auth->is('admin'); // returns true
Session invalidation
The user’s authentication checksum is stored in the session and verified on each request. On a mismatch, the user is automatically logged out of the session.
Using the hashed password for the checksum means that user will be logged out of all sessions after a password change. To keep him logged in, in the current session, call recalc()
.
Alternatively, you can generate a random checksum. This would allow you to explicitly force the invalidation of other sessions (for instance via the press of a button).
use Jasny\Auth;
class User implements Auth\UserInterface
{
public string $id;
public string $username;
public int $accessLevel = 0;
protected string $hashedPassword;
protected string $authChecksum;
// ...
public function forceLogout(): void
{
$this->authChecksum = bin2hex(random_bytes(32));
}
public function getAuthChecksum(): string
{
return $this->authChecksum;
}
}
To log out for all sessions except the current:
$auth->user()->forceLogout();
save_user_to_db($auth->user());
$auth->recalc();