fix(security): emit authentication success events

Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
2026-08-06 00:08:58 -04:00
parent 5b3e8f6588
commit b14bd302a3
8 changed files with 183 additions and 52 deletions
+8 -1
View File
@@ -52,8 +52,15 @@ class Module extends ModuleInstanceAbstract implements ModuleConsoleInterface, M
priority: 100,
);
foreach ([
$this->events->listen(
'core',
AuthenticationSucceededEvent::class,
FirewallService::class,
'logAuthenticationSuccess',
DeliveryMode::Deferred,
);
foreach ([
AccessDeniedEvent::class,
BruteForceDetectedEvent::class,
RateLimitExceededEvent::class,
+16 -1
View File
@@ -9,6 +9,7 @@ use KTXC\Resource\ProviderManager;
use KTXC\Security\Authentication\AuthenticationRequest;
use KTXC\Security\Authentication\AuthenticationResponse;
use KTXC\Security\Event\AuthenticationFailedEvent;
use KTXC\Security\Event\AuthenticationSucceededEvent;
use KTXC\Service\TokenService;
use KTXC\Service\UserAccountsService;
use KTXC\Context\TenantContextInterface;
@@ -616,7 +617,16 @@ class AuthenticationManager
*/
private function completeAuthentication(AuthenticationSession $session): AuthenticationResponse
{
$userData = $this->userService->fetchByIdentifier($session->userIdentifier);
$userId = $session->userIdentifier;
if ($userId === null) {
return AuthenticationResponse::failed(
AuthenticationResponse::ERROR_INVALID_SESSION,
'Authenticated user is missing',
401,
);
}
$userData = $this->userService->fetchByIdentifier($userId);
if ($userData === null) {
return AuthenticationResponse::failed(
@@ -633,6 +643,11 @@ class AuthenticationManager
$this->deleteSession($session->id);
$this->events->dispatch(new AuthenticationSucceededEvent(
$userId,
$session->tenantIdentifier,
));
return AuthenticationResponse::success(
$this->buildUserData($user),
$tokens
@@ -6,17 +6,12 @@ namespace KTXC\Security\Event;
use KTXF\Event\Event;
final class AuthenticationSucceededEvent extends Event implements SecurityRequestEventInterface
final class AuthenticationSucceededEvent extends Event implements SecurityEventInterface
{
public function __construct(
private readonly string $ipAddress,
private readonly string $userId,
?string $tenantId = null,
private readonly ?string $deviceFingerprint = null,
) {
if ($ipAddress === '') {
throw new \InvalidArgumentException('Successful authentication requires an IP address.');
}
if ($userId === '') {
throw new \InvalidArgumentException('Successful authentication requires a user ID.');
}
@@ -28,31 +23,6 @@ final class AuthenticationSucceededEvent extends Event implements SecurityReques
);
}
public function getIpAddress(): string
{
return $this->ipAddress;
}
public function getDeviceFingerprint(): ?string
{
return $this->deviceFingerprint;
}
public function getUserAgent(): ?string
{
return null;
}
public function getRequestPath(): ?string
{
return null;
}
public function getRequestMethod(): ?string
{
return null;
}
public function getUserId(): string
{
return $this->userId;
+8
View File
@@ -249,6 +249,14 @@ class FirewallService
}
}
public function logAuthenticationSuccess(AuthenticationSucceededEvent $event): void
{
$log = $this->securityLog($event, $this->requestContext->current());
if ($log !== null) {
$this->store->createLog($log);
}
}
private function securityLog(
SecurityEventInterface $event,
?Request $request = null,