Authentication Errors

In order to control the events that occur before or after the user login, the App\Authentication\JwtAuthentication class simply handles the following errors if it occurs for the user without requiring any event class.

Key Description
AUTHENTICATION_REQUIRED Authentication required. Please log in to your account.
USERNAME_OR_PASSWORD_FIELDS_NOT_GIVEN Username and password fields must be entered.
USERNAME_OR_PASSWORD_INCORRECT Username or password is incorrect.
ACCOUNT_IS_INACTIVE_OR_SUSPENDED This account is pending approval or has been suspended.
NO_ROLE_DEFINED_ON_THE_ACCOUNT There is no role defined for this user.
IP_VALIDATION_FAILED Your IP address could not be verified and has been logged out for security reasons.
USER_AGENT_VALIDATION_FAILED Your browser could not be verified and has been logged out for security reasons.

If you are thinking of making an improvement after the authentication process, you can take a look at the initAuthentication method.

public function initAuthentication(ServerRequestInterface $request) : ?UserInterface
{
    $post = $request->getParsedBody();
    $usernameField = $this->config['authentication']['form']['username'];
    $passwordField = $this->config['authentication']['form']['password'];

    // credentials are given ? 
    //
    if (! isset($post[$usernameField]) || ! isset($post[$passwordField])) {
        $this->error(Self::USERNAME_OR_PASSWORD_FIELDS_NOT_GIVEN);
        return null;
    }
    $this->authAdapter->setIdentity($post[$usernameField]);
    $this->authAdapter->setCredential($post[$passwordField]);

    $eventParams = [
        'request' => $request,
        'username' => $post[$usernameField],
    ];
    // credentials are correct ? 
    //
    $result = $this->authAdapter->authenticate();
    if (! $result->isValid()) {
        //
        // failed attempts event start
        //
        $results = $this->events->trigger(LoginListener::onFailedLogin, null, $eventParams);
        $failedResponse = $results->last();
        if ($failedResponse['banned']) {
            $this->error($failedResponse['message']);
            return null;
        }
        //
        // default behaviour
        //
        $this->error(Self::USERNAME_OR_PASSWORD_INCORRECT);
        return null;
    }
    $rowObject = $this->authAdapter->getResultRowObject();
    //
    // successful login event
    //
    $this->events->trigger(LoginListener::onSuccessfullLogin, null, $eventParams);
    //
    // user is active ? 
    //
    if (empty($rowObject->active)) {
        $this->error(Self::ACCOUNT_IS_INACTIVE_OR_SUSPENDED);
        return null;
    }
    //
    // is the role exists ?
    // 
    $roles = $this->authModel->findRolesById($rowObject->userId);
    if (empty($roles)) {
        $this->error(Self::NO_ROLE_DEFINED_ON_THE_ACCOUNT);
        return null;
    }
    $details = [
        'email' => $rowObject->email,
        'fullname' => $rowObject->fullname,
        'ip' => $this->getIpAddress(),
        'deviceKey' => $this->getDeviceKey($request),
    ];
    return ($this->userFactory)(
        $rowObject->userId,
        $result->getIdentity(),
        $roles,
        $details
    );
}

If you are planning to make more extensive arrangements for the situations described above or situations not explained here, you can use the Laminas EventManager class. You can perform advanced event management.