# Auth

**Authentication for PHP. Simple, lightweight and secure.**

Written once, to be used everywhere.

Completely framework-agnostic and database-agnostic.

## Why do I need this?

 * There are [tons](https://www.troyhunt.com/whos-who-of-bad-password-practices/) [of](https://blog.codinghorror.com/password-rules-are-bullshit/) [websites](https://badpasswordpolicies.tumblr.com/) with weak authentication systems. Don’t build such a site.
 * Re-implementing a new authentication system for every PHP project is *not* a good idea.
 * Building your own authentication classes piece by piece, and copying it to every project, is *not* recommended, either.
 * A secure authentication system with an easy-to-use API should be thoroughly designed and planned.
 * Peer-review for your critical infrastructure is *a must*.

## Requirements

 * PHP 5.6.0+
   * PDO (PHP Data Objects) extension (`pdo`)
     * MySQL Native Driver (`mysqlnd`) **or** PostgreSQL driver (`pgsql`) **or** SQLite driver (`sqlite`)
   * OpenSSL extension (`openssl`)
 * MySQL 5.5.3+ **or** MariaDB 5.5.23+ **or** PostgreSQL 9.5.10+ **or** SQLite 3.14.1+ **or** [other SQL databases](../Database)

## Installation

    ```
    $ composer require nikolaimv77/auth
    ```

 1. Include the Composer autoloader:

    ```php
    require __DIR__ . '/vendor/autoload.php';
    ```

 1. Set up a database and create the required tables:

    * [MariaDB](Database/MySQL.sql)
    * [MySQL](Database/MySQL.sql)
    * [PostgreSQL](Database/PostgreSQL.sql)
    * [SQLite](Database/SQLite.sql)

## Upgrading

Migrating from an earlier version of this project? See our [upgrade guide](Migration.md) for help.

## Usage

 * [Создание нового экземпляра](#creating-a-new-instance)
 * [Регистрация (sign up)](#registration-sign-up)
 * [Авторизоваться (sign in)](#login-sign-in)
 * [Email Подтверждени](#email-verification)
 * [Сохранение входа пользователя в систему](#keeping-the-user-logged-in)
 * [Восстановление пароля (“forgot password”)](#password-reset-forgot-password)
   * [Инициирование запроса](#step-1-of-3-initiating-the-request)
   * [Verifying an attempt](#step-2-of-3-verifying-an-attempt)
   * [Updating the password](#step-3-of-3-updating-the-password)
 * [Changing the current user’s password](#changing-the-current-users-password)
 * [Changing the current user’s email address](#changing-the-current-users-email-address)
 * [Re-sending confirmation requests](#re-sending-confirmation-requests)
 * [Logout](#logout)
 * [Accessing user information](#accessing-user-information)
   * [Login state](#login-state)
   * [User ID](#user-id)
   * [Email address](#email-address)
   * [Display name](#display-name)
   * [Status information](#status-information)
   * [Checking whether the user was “remembered”](#checking-whether-the-user-was-remembered)
   * [IP address](#ip-address)
   * [Additional user information](#additional-user-information)
 * [Reconfirming the user’s password](#reconfirming-the-users-password)
 * [Roles (or groups)](#roles-or-groups)
   * [Checking roles](#checking-roles)
   * [Available roles](#available-roles)
   * [Permissions (or access rights, privileges or capabilities)](#permissions-or-access-rights-privileges-or-capabilities)
   * [Custom role names](#custom-role-names)
 * [Enabling or disabling password resets](#enabling-or-disabling-password-resets)
 * [Throttling or rate limiting](#throttling-or-rate-limiting)
 * [Administration (managing users)](#administration-managing-users)
   * [Creating new users](#creating-new-users)
   * [Deleting users](#deleting-users)
   * [Assigning roles to users](#assigning-roles-to-users)
   * [Taking roles away from users](#taking-roles-away-from-users)
   * [Checking roles](#checking-roles-1)
   * [Impersonating users (logging in as user)](#impersonating-users-logging-in-as-user)
   * [Changing a user’s password](#changing-a-users-password)
 * [Cookies](#cookies)
   * [Renaming the library’s cookies](#renaming-the-librarys-cookies)
   * [Defining the domain scope for cookies](#defining-the-domain-scope-for-cookies)
   * [Restricting the path where cookies are available](#restricting-the-path-where-cookies-are-available)
   * [Controlling client-side script access to cookies](#controlling-client-side-script-access-to-cookies)
   * [Configuring transport security for cookies](#configuring-transport-security-for-cookies)
 * [Утилиты](#utilities)
   * [Создание случайной строки](#creating-a-random-string)
   * [Создание UUID v4 согласно RFC 4122](#creating-a-uuid-v4-as-per-rfc-4122)
 * [Чтение и запись session data](#reading-and-writing-session-data)

### Creating a new instance

```php
// $db = new \PDO('mysql:dbname=my-database;host=localhost;charset=utf8mb4', 'my-username', 'my-password');
// or
// $db = new \PDO('pgsql:dbname=my-database;host=localhost;port=5432', 'my-username', 'my-password');
// or
// $db = new \PDO('sqlite:../Databases/my-database.sqlite');

// or

// $db = \Mnv\Db\PdoDatabase::fromDsn(new \Mnv\Db\PdoDsn('mysql:dbname=my-database;host=localhost;charset=utf8mb4', 'my-username', 'my-password'));
// or
// $db = \Mnv\Db\PdoDatabase::fromDsn(new \Mnv\Db\PdoDsn('pgsql:dbname=my-database;host=localhost;port=5432', 'my-username', 'my-password'));
// or
// $db = \Mnv\Db\PdoDatabase::fromDsn(new \Mnv\Db\PdoDsn('sqlite:../Databases/my-database.sqlite'));

$auth = new \Mnv\Auth\Auth($db);
```

Если у вас уже есть открытое соединение `PDO`, просто используйте его повторно. Пользователю базы данных (например, my-username) необходимы как минимум привилегии SELECT, INSERT, UPDATE и DELETE для таблиц, используемых этой библиотекой (или их родительской базой данных).

Если ваш веб-сервер находится за прокси-сервером и `$ _SERVER ['REMOTE_ADDR']` содержит только IP-адрес прокси, вы должны передать реальный IP-адрес пользователя конструктору во втором аргументе, который называется `$ipAddress`. По умолчанию используется обычный удаленный IP-адрес, полученный PHP.

Если вашим таблицам базы данных для этой библиотеки нужен общий префикс, например my_users вместо users (и аналогично для других таблиц) передайте префикс (например, my_) в качестве третьего параметра конструктору, который называется `$dbTablePrefix`. Это необязательно, и по умолчанию префикс пуст.

Во время разработки вы можете отключить ограничение или регулирование запросов, выполняемое этой библиотекой. Для этого передайте конструктору `false` в качестве четвертого аргумента, который называется` $ дросселирование`. По умолчанию функция включена.

Во время существования сеанса некоторые пользовательские данные могут быть изменены удаленно либо клиентом в другом сеансе, либо администратором. Это означает, что эту информацию необходимо регулярно повторно синхронизировать с ее официальным источником в базе данных, что эта библиотека выполняет автоматически. По умолчанию это происходит каждые пять минут. Если вы хотите изменить этот интервал, передайте настраиваемый интервал в секундах конструктору в качестве пятого аргумента, который называется `$ sessionResyncInterval`.

Если для всех ваших таблиц базы данных требуется общее имя базы данных, имя схемы или другой квалификатор, который должен быть указан явно, вы можете дополнительно передать этот квалификатор конструктору в качестве шестого параметра, который называется `$dbSchema`.

### Регистрация (sign up)

```php
try {
    $userId = $auth->register($_POST['email'], $_POST['password'], $_POST['username'], function ($selector, $token) {
        echo 'Send ' . $selector . ' and ' . $token . ' to the user (e.g. via email)';
    });

    echo 'We have signed up a new user with the ID ' . $userId;
}
catch (\Mnv\Auth\InvalidEmailException $e) {
    die('Invalid email address');
}
catch (\Mnv\Auth\InvalidPasswordException $e) {
    die('Invalid password');
}
catch (\Mnv\Auth\UserAlreadyExistsException $e) {
    die('User already exists');
}
catch (\Mnv\Auth\TooManyRequestsException $e) {
    die('Too many requests');
}
```

** Примечание. ** Функция анонимного обратного вызова - это [закрытие] (https://www.php.net/manual/functions.anonymous.php). Таким образом, помимо собственных параметров, только [суперглобальные переменные] (https://www.php.net/manual/language.variables.superglobals.php), такие как `$ _GET`,` $ _POST`, `$ _COOKIE` и` $ _SERVER` доступны внутри. Для любой другой переменной из родительской области вам необходимо явно сделать копию доступной внутри, добавив предложение `use` после списка параметров.

Имя пользователя в третьем параметре необязательно. Вы можете передать там `null`, если не хотите управлять именами пользователей.

С другой стороны, если вы хотите принудительно использовать уникальные имена пользователей, просто вызовите «registerWithUniqueUsername» вместо «register» и будьте готовы перехватить «DuplicateUsernameException».

** Примечание. ** При принятии и управлении именами пользователей вы можете исключить непечатаемые управляющие символы и некоторые печатаемые специальные символы, как в классе символов `[\ x00- \ x1f \ x7f \ /: \\]`. Для этого вы можете заключить вызов Auth # register или Auth # registerWithUniqueUsername в условную ветвь, например, принимая имена пользователей только при выполнении следующего условия:

```php
if (\preg_match('/[\x00-\x1f\x7f\/:\\\\]/', $username) === 0) {
    // ...
}
```

Для проверки электронной почты вы должны создать URL-адрес с селектором и токеном и отправить его пользователю, например:

```php
$url = 'https://www.example.com/verify_email?selector=' . \urlencode($selector) . '&token=' . \urlencode($token);
```

Если вы не хотите выполнять проверку адреса электронной почты, просто опустите последний параметр в поле `Auth # register`. Тогда новый пользователь станет активным немедленно.

Нужно хранить дополнительную информацию о пользователе? Прочтите [здесь] (# дополнительная-информация-пользователя).

** Примечание. ** При отправке электронного письма пользователю обратите внимание, что (необязательное) имя пользователя на данный момент еще не подтверждено как приемлемое для владельца (нового) адреса электронной почты. Он может содержать оскорбительную или вводящую в заблуждение формулировку, выбранную кем-то, кто на самом деле не является владельцем адреса.


### Login (sign in)

```php
try {
    $auth->login($_POST['email'], $_POST['password']);

    echo 'User is logged in';
}
catch (\Mnv\Auth\InvalidEmailException $e) {
    die('Wrong email address');
}
catch (\Mnv\Auth\InvalidPasswordException $e) {
    die('Wrong password');
}
catch (\Mnv\Auth\EmailNotVerifiedException $e) {
    die('Email not verified');
}
catch (\Mnv\Auth\TooManyRequestsException $e) {
    die('Too many requests');
}
```

If you want to sign in with usernames on the other hand, either in addition to the login via email address or as a replacement, that’s possible as well. Simply call the method `loginWithUsername` instead of method `login`. Then, instead of catching `InvalidEmailException`, make sure to catch both `UnknownUsernameException` and `AmbiguousUsernameException`. You may also want to read the notes about the uniqueness of usernames in the section that explains how to [sign up new users](#registration-sign-up).

### Email verification / Подтверждение адреса электронной почты

Извлеките селектор и токен из URL-адреса, который пользователь щелкнул в проверочном электронном письме.

```php
try {
    $auth->confirmEmail($_GET['selector'], $_GET['token']);

    echo 'Email address has been verified';
}
catch (\Mnv\Auth\InvalidSelectorTokenPairException $e) {
    die('Invalid token');
}
catch (\Mnv\Auth\TokenExpiredException $e) {
    die('Token expired');
}
catch (\Mnv\Auth\UserAlreadyExistsException $e) {
    die('Email address already exists');
}
catch (\Mnv\Auth\TooManyRequestsException $e) {
    die('Too many requests');
}
```

Если вы хотите, чтобы пользователь автоматически входил в систему после успешного подтверждения, просто вызовите «confirmEmailAndSignIn» вместо «confirmEmail». Этот альтернативный метод также поддерживает [постоянный вход в систему] (#keep-the-user-logged-in) через необязательный третий параметр.

В случае успеха оба метода `confirmEmail` и `confirmEmailAndSignIn` возвращают массив с новым адресом электронной почты пользователя, который только что был подтвержден, с индексом один. Если подтверждение было для изменения адреса, а не для простой проверки адреса, старый адрес электронной почты пользователя будет включен в массив с нулевым индексом.

### Keeping the user logged in / Сохранение входа пользователя в систему

Третий параметр методов `Auth#login` и `Auth#confirmEmailAndSignIn` контролирует, является ли логин постоянным с долгоживущим файлом cookie. При таком постоянном входе в систему пользователи могут оставаться аутентифицированными в течение длительного времени, даже если сеанс браузера уже закрыт и срок действия файлов cookie сеанса истек. Как правило, вы хотите, чтобы пользователь оставался в системе в течение нескольких недель или месяцев с помощью этой функции, которая известна как «запомнить меня» или «держать меня в системе». Многие пользователи сочтут это более удобным, но могут оказаться менее безопасными, если они оставят свои устройства без присмотра.

```php
if ($_POST['remember'] == 1) {
    // keep logged in for one year
    $rememberDuration = (int) (60 * 60 * 24 * 365.25);
}
else {
    // do not keep logged in after session ends
    $rememberDuration = null;
}

// ...

$auth->login($_POST['email'], $_POST['password'], $rememberDuration);

// ...
```

*Without* the persistent login, which is the *default* behavior, a user will only stay logged in until they close their browser, or as long as configured via `session.cookie_lifetime` and `session.gc_maxlifetime` in PHP.

Omit the third parameter or set it to `null` to disable the feature. Otherwise, you may ask the user whether they want to enable “remember me”. This is usually done with a checkbox in your user interface. Use the input from that checkbox to decide between `null` and a pre-defined duration in seconds here, e.g. `60 * 60 * 24 * 365.25` for one year.

### Password reset (“forgot password”) / Восстановление пароля

#### Step 1 of 3: Initiating the request / Шаг 1 из 3. Отправка запроса

```php
try {
    $auth->forgotPassword($_POST['email'], function ($selector, $token) {
        echo 'Send ' . $selector . ' and ' . $token . ' to the user (e.g. via email)';
    });

    echo 'Request has been generated';
}
catch (\Mnv\Auth\InvalidEmailException $e) {
    die('Invalid email address');
}
catch (\Mnv\Auth\EmailNotVerifiedException $e) {
    die('Email not verified');
}
catch (\Mnv\Auth\ResetDisabledException $e) {
    die('Password reset is disabled');
}
catch (\Mnv\Auth\TooManyRequestsException $e) {
    die('Too many requests');
}
```

** Примечание. ** Функция анонимного обратного вызова - это [закрытие] (https://www.php.net/manual/functions.anonymous.php). Таким образом, помимо собственных параметров, только [суперглобальные переменные] (https://www.php.net/manual/language.variables.superglobals.php), 
такие как `$ _GET`,` $ _POST`, `$ _COOKIE` и` $ _SERVER` доступны внутри. Для любой другой переменной из родительской области вам необходимо явно сделать копию доступной внутри, добавив предложение `use` после списка параметров.

Вы должны создать URL-адрес с селектором и токеном и отправить его пользователю, например:

```php
$url = 'https://www.example.com/reset_password?selector=' . \urlencode($selector) . '&token=' . \urlencode($token);
```

Если время жизни запросов на сброс пароля по умолчанию не работает для вас, вы можете использовать третий параметр `Auth#hibitedPassword`, чтобы указать настраиваемый интервал в секундах, после которого запросы должны истечь.
#### Step 2 of 3: Verifying an attempt / Шаг 2 из 3. Проверка попытки

As the next step, users will click on the link that they received. Extract the selector and token from the URL.

If the selector/token pair is valid, let the user choose a new password:

```php
try {
    $auth->canResetPasswordOrThrow($_GET['selector'], $_GET['token']);

    echo 'Put the selector into a "hidden" field (or keep it in the URL)';
    echo 'Put the token into a "hidden" field (or keep it in the URL)';

    echo 'Ask the user for their new password';
}
catch (\Mnv\Auth\InvalidSelectorTokenPairException $e) {
    die('Invalid token');
}
catch (\Mnv\Auth\TokenExpiredException $e) {
    die('Token expired');
}
catch (\Mnv\Auth\ResetDisabledException $e) {
    die('Password reset is disabled');
}
catch (\Mnv\Auth\TooManyRequestsException $e) {
    die('Too many requests');
}
```

В качестве альтернативы, если вам не нужны сообщения об ошибках, а нужно только проверить их действительность, вы можете использовать более простую версию:

```php
if ($auth->canResetPassword($_GET['selector'], $_GET['token'])) {
    echo 'Put the selector into a "hidden" field (or keep it in the URL)';
    echo 'Put the token into a "hidden" field (or keep it in the URL)';

    echo 'Ask the user for their new password';
}
```

#### Step 3 of 3: Updating the password / Шаг 3 из 3. Обновление пароля

Теперь, когда у вас есть новый пароль для пользователя (и все еще есть две другие части информации), вы можете сбросить пароль:

```php
try {
    $auth->resetPassword($_POST['selector'], $_POST['token'], $_POST['password']);

    echo 'Password has been reset';
}
catch (\Mnv\Auth\InvalidSelectorTokenPairException $e) {
    die('Invalid token');
}
catch (\Mnv\Auth\TokenExpiredException $e) {
    die('Token expired');
}
catch (\Mnv\Auth\ResetDisabledException $e) {
    die('Password reset is disabled');
}
catch (\Mnv\Auth\InvalidPasswordException $e) {
    die('Invalid password');
}
catch (\Mnv\Auth\TooManyRequestsException $e) {
    die('Too many requests');
}
```

Вы хотите, чтобы соответствующий пользователь автоматически входил в систему после успешного сброса пароля? Просто используйте `Auth#resetPasswordAndSignIn` вместо `Auth#resetPassword`, чтобы сразу войти в систему.

Если вам нужен идентификатор пользователя или адрес электронной почты, например для отправки им уведомления о том, что их пароль был успешно сброшен, просто используйте возвращаемое значение Auth # resetPassword, которое представляет собой массив, содержащий две записи с именами `id` и` email`.

### Changing the current user’s password / Смена пароля текущего пользователя

Если пользователь в настоящее время вошел в систему, он может изменить свой пароль.

```php
try {
    $auth->changePassword($_POST['oldPassword'], $_POST['newPassword']);

    echo 'Password has been changed';
}
catch (\Mnv\Auth\NotLoggedInException $e) {
    die('Not logged in');
}
catch (\Mnv\Auth\InvalidPasswordException $e) {
    die('Invalid password(s)');
}
catch (\Mnv\Auth\TooManyRequestsException $e) {
    die('Too many requests');
}
```

Запрашивать у пользователя их текущий (а вскоре и * старый *) пароль и требовать его проверки - это рекомендуемый способ обработки изменений пароля. Это показано выше.

Однако если вы уверены, что вам не нужно это подтверждение, вы можете вызвать `changePasswordWithoutOldPassword` вместо `changePassword` и удалить первый параметр из вызова этого метода (который в противном случае содержал бы старый пароль).

В любом случае, после изменения пароля пользователя, вы должны отправить электронное письмо на основной адрес электронной почты его учетной записи в качестве внешнего уведомления, информирующего владельца учетной записи об этом критическом изменении.

### Changing the current user’s email address 
###Изменение адреса электронной почты текущего пользователя

####
Если пользователь в настоящее время вошел в систему, он может изменить свой адрес электронной почты.

```php
try {
    if ($auth->reconfirmPassword($_POST['password'])) {
        $auth->changeEmail($_POST['newEmail'], function ($selector, $token) {
            echo 'Send ' . $selector . ' and ' . $token . ' to the user (e.g. via email to the *new* address)';
        });

        echo 'The change will take effect as soon as the new email address has been confirmed';
    }
    else {
        echo 'We can\'t say if the user is who they claim to be';
    }
}
catch (\Mnv\Auth\InvalidEmailException $e) {
    die('Invalid email address');
}
catch (\Mnv\Auth\UserAlreadyExistsException $e) {
    die('Email address already exists');
}
catch (\Mnv\Auth\EmailNotVerifiedException $e) {
    die('Account not verified');
}
catch (\Mnv\Auth\NotLoggedInException $e) {
    die('Not logged in');
}
catch (\Mnv\Auth\TooManyRequestsException $e) {
    die('Too many requests');
}
```

** Примечание. ** Функция анонимного обратного вызова - это [закрытие] (https://www.php.net/manual/functions.anonymous.php). 
Таким образом, помимо собственных параметров, только [суперглобальные переменные] (https://www.php.net/manual/language.variables.superglobals.php), 
такие как `$ _GET`,` $ _POST`, `$ _COOKIE` и` $ _SERVER` доступны внутри. 
Для любой другой переменной из родительской области вам необходимо явно сделать копию доступной внутри, добавив предложение `use` после списка параметров.
####
Для проверки электронной почты вы должны создать URL-адрес с селектором и токеном и отправить его пользователю, например:

```php
$url = 'https://www.example.com/verify_email?selector=' . \urlencode($selector) . '&token=' . \urlencode($token);
```

** Примечание. ** 
При отправке электронного письма пользователю обратите внимание, что (необязательное) имя пользователя на данный момент еще не подтверждено как приемлемое для владельца (нового) адреса электронной почты. 
Он может содержать оскорбительную или вводящую в заблуждение формулировку, выбранную кем-то, кто на самом деле не является владельцем адреса.

После того, как был сделан запрос на изменение адреса электронной почты, или, что еще лучше, после того, как изменение было подтверждено пользователем, вы должны отправить электронное письмо на * предыдущий * адрес электронной почты его учетной записи в качестве внешнего уведомления, информирующего учетную запись. владелец об этом критическом изменении.

** Примечание. ** Как и ожидалось, изменения адреса электронной почты пользователя сразу же вступают в силу в локальном сеансе. 
Однако в других сеансах (например, на других устройствах) изменениям может потребоваться до пяти минут, чтобы они вступили в силу. 
Это увеличивает производительность и обычно не вызывает проблем. 
Тем не менее, если вы хотите изменить это поведение, просто уменьшите (или, возможно, увеличьте) значение, которое вы передаете конструктору [`Auth`] (# create-a-new-instance) в качестве аргумента с именем `$sessionResyncInterval`.


### Re-sending confirmation requests 
### Повторная отправка запросов на подтверждение

Если предыдущий запрос подтверждения не может быть доставлен пользователю, или если пользователь пропустил этот запрос, 
или если они просто не хотят больше ждать, вы можете повторно отправить более ранний запрос, например:

```php
try {
    $auth->resendConfirmationForEmail($_POST['email'], function ($selector, $token) {
        echo 'Send ' . $selector . ' and ' . $token . ' to the user (e.g. via email)';
    });

    echo 'The user may now respond to the confirmation request (usually by clicking a link)';
}
catch (\Mnv\Auth\ConfirmationRequestNotFound $e) {
    die('No earlier request found that could be re-sent');
}
catch (\Mnv\Auth\TooManyRequestsException $e) {
    die('There have been too many requests -- try again later');
}
```

Если вы хотите указать пользователя по его идентификатору, а не по адресу электронной почты, это тоже возможно:

```php
try {
    $auth->resendConfirmationForUserId($_POST['userId'], function ($selector, $token) {
        echo 'Send ' . $selector . ' and ' . $token . ' to the user (e.g. via email)';
    });

    echo 'The user may now respond to the confirmation request (usually by clicking a link)';
}
catch (\Mnv\Auth\ConfirmationRequestNotFound $e) {
    die('No earlier request found that could be re-sent');
}
catch (\Mnv\Auth\TooManyRequestsException $e) {
    die('There have been too many requests -- try again later');
}
```

** Примечание. ** Функция анонимного обратного вызова - это [закрытие] (https://www.php.net/manual/functions.anonymous.php). 
Таким образом, помимо собственных параметров, только [суперглобальные переменные] (https://www.php.net/manual/language.variables.superglobals.php), такие как `$ _GET`,` $ _POST`, `$ _COOKIE` и` $ _SERVER` доступны внутри. 
Для любой другой переменной из родительской области вам необходимо явно сделать копию доступной внутри, добавив предложение `use` после списка параметров.

Обычно вы должны создать URL-адрес с селектором и токеном и отправить его пользователю, например следующее:

```php
$url = 'https://www.example.com/verify_email?selector=' . \urlencode($selector) . '&token=' . \urlencode($token);
```

** Примечание. ** При отправке электронного письма пользователю обратите внимание, что (необязательное) имя пользователя на данный момент еще не подтверждено как приемлемое для владельца (нового) адреса электронной почты. Он может содержать оскорбительную или вводящую в заблуждение формулировку, выбранную кем-то, кто на самом деле не является владельцем адреса.

### Logout / Выйти

```php
$auth->logOut();

// или

try {
    $auth->logOutEverywhereElse();
}
catch (\Mnv\Auth\NotLoggedInException $e) {
    die('Не авторизован');
}

// или

try {
    $auth->logOutEverywhere();
}
catch (\Mnv\Auth\NotLoggedInException $e) {
    die('Не авторизован');
}
```

Кроме того, если вы также храните пользовательскую информацию в сеансе и хотите, чтобы эта информация была удалена, вы можете уничтожить весь сеанс, вызвав второй метод:
```php
$auth->destroySession();
```

** Примечание. ** Как и ожидалось, глобальный выход из системы немедленно вступает в силу в локальном сеансе. 
Однако в других сеансах (например, на других устройствах) изменениям может потребоваться до пяти минут, чтобы они вступили в силу. 
Это увеличивает производительность и обычно не вызывает проблем. 
Тем не менее, если вы хотите изменить это поведение, просто уменьшите (или, возможно, увеличьте) значение, которое вы передаете конструктору [`Auth`] (# create-a-new-instance) 
в качестве аргумента с именем `$sessionResyncInterval`.


### Accessing user information / Доступ к информации о пользователе

#### Login state 
#### Состояние входа

```php
if ($auth->isLoggedIn()) {
    echo 'User is signed in';
}
else {
    echo 'User is not signed in yet';
}
```

Сокращение / псевдоним для этого метода: `$auth->check()`.

#### User ID

```php
$id = $auth->getUserId();
```

Если пользователь в настоящее время не вошел в систему, возвращается `null`.

Сокращение / псевдоним для этого метода:  `$auth->id()`.

#### Email address

```php
$email = $auth->getEmail();
```

Если пользователь в настоящее время не вошел в систему, возвращается `null`.

#### Display name

```php
$username = $auth->getUsername();
```

Помните, что имена пользователей не являются обязательными и есть только имя пользователя, если вы указали его при регистрации.

Если пользователь в настоящее время не вошел в систему, возвращается значение `null`.

#### Status information 
#### Информация о статусе

```php
if ($auth->isNormal()) {
    echo 'User is in default state';
}

if ($auth->isArchived()) {
    echo 'User has been archived';
}

if ($auth->isBanned()) {
    echo 'User has been banned';
}

if ($auth->isLocked()) {
    echo 'User has been locked';
}

if ($auth->isPendingReview()) {
    echo 'User is pending review';
}

if ($auth->isSuspended()) {
    echo 'User has been suspended';
}
```

#### Checking whether the user was “remembered” 
#### Проверка, «запомнился» ли пользователь

```php
if ($auth->isRemembered()) {
    echo 'User did not sign in but was logged in through their long-lived cookie';
}
else {
    echo 'User signed in manually';
}
```

Если пользователь в настоящее время не вошел в систему, возвращается `null`.

#### IP address

```php
$ip = $auth->getIpAddress();
```

#### Additional user information / Дополнительная информация о пользователе

Чтобы сохранить пригодность этой библиотеки для всех целей, а также ее полную возможность многократного использования, в ней нет дополнительных столбцов, содержащих информацию о пользователях. Но без дополнительной информации о пользователе, конечно же, не обойтись:

Вот как можно использовать эту библиотеку с вашими собственными таблицами для пользовательской информации с удобством обслуживания и повторного использования:

1. Добавьте любое количество настраиваемых таблиц базы данных, в которых вы храните настраиваемую информацию о пользователях, например таблица с именем `profiles`.
1. Каждый раз, когда вы вызываете метод `register` (который возвращает идентификатор нового пользователя), добавьте впоследствии свою собственную логику, которая заполняет ваши настраиваемые таблицы базы данных.
1. Если пользовательская информация нужна вам редко, вы можете просто получить ее по мере необходимости. Однако, если он вам нужен чаще, вы, вероятно, захотите включить его в данные сеанса. Следующий метод - это надежный способ загрузки данных и доступа к ним:
  
  
    ```php
    function getUserInfo(\Mnv\Auth\Auth $auth) {
        if (!$auth->isLoggedIn()) {
            return null;
        }

        if (!isset($_SESSION['_internal_user_info'])) {
            // TODO: load your custom user information and assign it to the session variable below
            // $_SESSION['_internal_user_info'] = ...
        }

        return $_SESSION['_internal_user_info'];
    }
    ```

### Reconfirming the user’s password 
### Повторное подтверждение пароля пользователя

Всякий раз, когда вы хотите снова подтвердить личность пользователя, 
например прежде чем пользователю будет разрешено выполнить какое-либо 
«опасное» действие, вы должны еще раз подтвердить его пароль, чтобы подтвердить, 
что он на самом деле тот, за кого себя выдает.

Например, когда пользователь был запомнен долгоживущим файлом cookie и, 
таким образом, `Auth#isRemembered` возвращает true, 
это означает, что пользователь, вероятно, не вводил свой пароль в течение некоторого времени.
В этом случае вы можете подтвердить их пароль.

```php
try {
    if ($auth->reconfirmPassword($_POST['password'])) {
        echo 'The user really seems to be who they claim to be';
    }
    else {
        echo 'We can\'t say if the user is who they claim to be';
    }
}
catch (\Mnv\Auth\NotLoggedInException $e) {
    die('The user is not signed in');
}
catch (\Mnv\Auth\TooManyRequestsException $e) {
    die('Too many requests');
}
```

### Roles (or groups) / Роли (или группы)

У каждого пользователя может быть любое количество ролей, которые вы можете использовать для реализации авторизации и улучшения контроля доступа.

Пользователи могут вообще не иметь роли (что они делают по умолчанию), иметь только одну роль или любую произвольную комбинацию ролей.


#### Checking roles / Проверка ролей

```php
if ($auth->hasRole(\Mnv\Auth\Role::SUPER_MODERATOR)) {
    echo 'The user is a super moderator';
}

// or

if ($auth->hasAnyRole(\Mnv\Auth\Role::DEVELOPER, \Mnv\Auth\Role::MANAGER)) {
    echo 'The user is either a developer, or a manager, or both';
}

// or

if ($auth->hasAllRoles(\Mnv\Auth\Role::DEVELOPER, \Mnv\Auth\Role::MANAGER)) {
    echo 'The user is both a developer and a manager';
}
```

В то время как метод `hasRole` принимает ровно одну роль в качестве аргумента, 
два метода `hasAnyRole` и `hasAllRoles` могут принимать любое количество ролей, 
которые вы хотите проверить.

В качестве альтернативы вы можете получить список всех ролей, 
которые были назначены пользователю:

```php
$auth->getRoles();
```

#### Available roles 
#### Доступные роли

```php

```


Вы можете использовать любую из этих ролей и игнорировать те, которые вам не нужны. 
Приведенный выше список также можно получить программно в одном из трех форматов:


```php
\Mnv\Auth\Role::getMap();
// or
\Mnv\Auth\Role::getNames();
// or
\Mnv\Auth\Role::getValues();
```

#### Permissions (or access rights, privileges or capabilities)

Разрешения каждого пользователя кодируются так, как требования ролей указываются во всей базе кода. 
Если эти требования оцениваются с помощью набора ролей конкретного пользователя, в результате получаются неявно проверенные разрешения.

Для более крупных проектов часто рекомендуется хранить определение разрешений в одном месте. 
Тогда вы не проверяете * роли * в своей бизнес-логике, а проверяете * отдельные разрешения *. 
Вы можете реализовать эту концепцию следующим образом:


```php
function canEditArticle(\Mnv\Auth\Auth $auth) {
    return $auth->hasAnyRole(
        \Mnv\Auth\Role::MODERATOR,
        \Mnv\Auth\Role::SUPER_MODERATOR,
        \Mnv\Auth\Role::ADMIN,
        \Mnv\Auth\Role::SUPER_ADMIN
    );
}

// ...

if (canEditArticle($auth)) {
    echo 'The user can edit articles here';
}

// ...

if (canEditArticle($auth)) {
    echo '... and here';
}

// ...

if (canEditArticle($auth)) {
    echo '... and here';
}
```

Как видите, разрешение на редактирование статьи определенным пользователем хранится в центральном месте. 
Эта реализация имеет два основных преимущества:

Если вы * хотите знать *, какие пользователи могут редактировать статьи, вам не нужно проверять свою бизнес-логику в разных местах, вам нужно только посмотреть, где определено конкретное разрешение. 
И если вы хотите * изменить *, кто может редактировать статью, вам нужно сделать это только в одном месте, а не во всей базе кода.

Но это также связано с немного большими накладными расходами при реализации ограничений доступа в первый раз, что может или не может стоить того для вашего проекта.


### Custom role names 
#### Настраиваемые имена ролей

Если имена включенных ролей вам не подходят, вы можете назначить псевдонимы любому количеству ролей, используя свои собственные идентификаторы, например нравится:


```php
namespace My\Namespace;

final class MyRole {

    const CUSTOMER_SERVICE_AGENT = \Mnv\Auth\Role::REVIEWER;
    const FINANCIAL_DIRECTOR = \Mnv\Auth\Role::COORDINATOR;

    private function __construct() {}

}
```

Приведенный выше пример позволит вам использовать

```php
\My\Namespace\MyRole::CUSTOMER_SERVICE_AGENT;
// and
\My\Namespace\MyRole::FINANCIAL_DIRECTOR;
```

вместо

```php
\Mnv\Auth\Role::REVIEWER;
// and
\Mnv\Auth\Role::COORDINATOR;
```

Просто помните, что * не * назначать * одну * включенную роль * нескольким * ролям с настраиваемыми именами.

### Enabling or disabling password resets / Включение или отключение сброса пароля

Хотя сброс пароля по электронной почте - это удобная функция, которую большинство пользователей время от времени находят полезной, доступность этой функции означает, что учетные записи в вашем сервисе защищены настолько же, насколько безопасны связанные учетные записи электронной почты пользователя.

Вы можете предоставить заботящимся о безопасности (и опытным) пользователям возможность отключить сброс паролей для своих учетных записей (и снова включить их позже) для повышения безопасности:

```php
try {
    if ($auth->reconfirmPassword($_POST['password'])) {
        $auth->setPasswordResetEnabled($_POST['enabled'] == 1);

        echo 'The setting has been changed';
    }
    else {
        echo 'We can\'t say if the user is who they claim to be';
    }
}
catch (\Mnv\Auth\NotLoggedInException $e) {
    die('The user is not signed in');
}
catch (\Mnv\Auth\TooManyRequestsException $e) {
    die('Too many requests');
}
```

Чтобы проверить текущее значение этого параметра, используйте возвращаемое значение из

```php
$auth->isPasswordResetEnabled();
```

для правильной опции по умолчанию в вашем пользовательском интерфейсе. Вам не нужно проверять это значение на наличие ограничений функции, которые применяются автоматически.

### Throttling or rate limiting 
### Дросселирование или ограничение скорости

Все методы, предоставляемые этой библиотекой, * автоматически * защищены от чрезмерного количества запросов от клиентов. Если возникнет необходимость, вы можете (временно) отключить эту защиту, используя параметр [`$throttling`] (#create-a-new-instance), переданный конструктору.

Если вы хотите также ограничить или ограничить скорость * внешние * функции или методы, например те, что в вашем собственном коде, вы можете использовать встроенный вспомогательный метод для дросселирования и ограничения скорости:

```php
try {
    // throttle the specified resource or feature to *3* requests per *60* seconds
    $auth->throttle([ 'my-resource-name' ], 3, 60);

    echo 'Do something with the resource or feature';
}
catch (\Mnv\Auth\TooManyRequestsException $e) {
    // operation cancelled

    \http_response_code(429);
    exit;
}
```

Если защита ресурса или функции должна дополнительно зависеть от другого атрибута, например чтобы отслеживать что-то отдельно по IP-адресу, просто добавьте дополнительные данные в описание ресурса, например:

```php
[ 'my-resource-name', $_SERVER['REMOTE_ADDR'] ]
// instead of
// [ 'my-resource-name' ]
```

Допустить короткие всплески активности во время пикового спроса можно, указав коэффициент всплеска в качестве четвертого аргумента. 
Например, значение «5» допускает временные всплески активности в пять раз по сравнению с общепринятым уровнем.

В некоторых случаях вы можете просто * смоделировать * дросселирование или ограничение скорости. 
Это позволяет вам проверить, будет ли действие разрешено, без фактического изменения трекера активности. 
Для этого просто передайте «true» в качестве пятого аргумента.

** Примечание. ** Когда вы отключаете регулирование в экземпляре (используя параметр [`$ throttling`] (#create-a-new-instance), переданный конструктору), это отключает как автоматическую внутреннюю защиту, так и эффект. любых вызовов `Auth#throttle` в вашем собственном коде приложения - если вы также не установили необязательный параметр `$force` равным `true` в определенных вызовах `Auth#throttle`.

### Administration (managing users) / Администрирование (управление пользователями)

The administrative interface is available via `$auth->admin()`. You can call various method on this interface, as documented below.

Do not forget to implement secure access control before exposing access to this interface. For example, you may provide access to this interface to logged in users with the administrator role only, or use the interface in private scripts only.

#### Creating new users 
#### Создание новых пользователей

```php
try {
    $userId = $auth->admin()->createUser($_POST['email'], $_POST['password'], $_POST['username']);

    echo 'We have signed up a new user with the ID ' . $userId;
}
catch (\Mnv\Auth\InvalidEmailException $e) {
    die('Invalid email address');
}
catch (\Mnv\Auth\InvalidPasswordException $e) {
    die('Invalid password');
}
catch (\Mnv\Auth\UserAlreadyExistsException $e) {
    die('User already exists');
}
```

Имя пользователя в третьем параметре необязательно.
Вы можете передать там `null`, если не хотите управлять именами пользователей.

С другой стороны, если вы хотите принудительно использовать уникальные имена пользователей, просто вызовите `createUserWithUniqueUsername` вместо `createUser` и будьте готовы перехватить `DuplicateUsernameException`.


#### Deleting users / Удаление пользователей

Deleting users by their ID: / Удаление пользователей по их ID:

```php
try {
    $auth->admin()->deleteUserById($_POST['id']);
}
catch (\Mnv\Auth\UnknownIdException $e) {
    die('Unknown ID');
}
```

Удаление пользователей по их адресу электронной почты:

```php
try {
    $auth->admin()->deleteUserByEmail($_POST['email']);
}
catch (\Mnv\Auth\InvalidEmailException $e) {
    die('Unknown email address');
}
```

Удаление пользователей по имени пользователя:

```php
try {
    $auth->admin()->deleteUserByUsername($_POST['username']);
}
catch (\Mnv\Auth\UnknownUsernameException $e) {
    die('Unknown username');
}
catch (\Mnv\Auth\AmbiguousUsernameException $e) {
    die('Ambiguous username');
}
```

#### Retrieving a list of registered users / Получение списка зарегистрированных пользователей

При получении списка всех пользователей требования сильно различаются между проектами и вариантами использования, и настройка является обычным делом. 
Например, вы можете захотеть получить разные столбцы, объединить связанные таблицы, отфильтровать по определенным критериям, 
изменить способ сортировки результатов (в разном направлении) и ограничить количество результатов (при указании смещения).

Вот почему проще использовать один настраиваемый SQL-запрос. Начнем со следующего:

```sql
SELECT id, email, username, status, verified, roles_mask, registered, last_login FROM users;
```

#### Assigning roles to users / Назначение ролей пользователям

```php
try {
    $auth->admin()->addRoleForUserById($userId, \Mnv\Auth\Role::ADMIN);
}
catch (\Mnv\Auth\UnknownIdException $e) {
    die('Unknown user ID');
}

// or

try {
    $auth->admin()->addRoleForUserByEmail($userEmail, \Mnv\Auth\Role::ADMIN);
}
catch (\Mnv\Auth\InvalidEmailException $e) {
    die('Unknown email address');
}

// or

try {
    $auth->admin()->addRoleForUserByUsername($username, \Mnv\Auth\Role::ADMIN);
}
catch (\Mnv\Auth\UnknownUsernameException $e) {
    die('Unknown username');
}
catch (\Mnv\Auth\AmbiguousUsernameException $e) {
    die('Ambiguous username');
}
```

** Примечание. ** Изменения в наборе ролей пользователя могут вступить в силу в течение пяти минут. 
Это увеличивает производительность и обычно не вызывает проблем. 
Тем не менее, если вы хотите изменить это поведение, просто уменьшите (или, возможно, увеличьте) значение, 
которое вы передаете конструктору [`Auth`] (#create-a-new-instance) в качестве аргумента с именем `$sessionResyncInterval`.

#### Taking roles away from users / Отнимаем роли у пользователей

```php
try {
    $auth->admin()->removeRoleForUserById($userId, \Mnv\Auth\Role::ADMIN);
}
catch (\Mnv\Auth\UnknownIdException $e) {
    die('Unknown user ID');
}

// or

try {
    $auth->admin()->removeRoleForUserByEmail($userEmail, \Mnv\Auth\Role::ADMIN);
}
catch (\Mnv\Auth\InvalidEmailException $e) {
    die('Unknown email address');
}

// or

try {
    $auth->admin()->removeRoleForUserByUsername($username, \Mnv\Auth\Role::ADMIN);
}
catch (\Mnv\Auth\UnknownUsernameException $e) {
    die('Unknown username');
}
catch (\Mnv\Auth\AmbiguousUsernameException $e) {
    die('Ambiguous username');
}
```

** Примечание. ** Изменения в наборе ролей пользователя могут вступить в силу в течение пяти минут. Это увеличивает производительность и обычно не вызывает проблем. 
Тем не менее, если вы хотите изменить это поведение, просто уменьшите (или, возможно, увеличьте) значение, которое вы передаете конструктору [`Auth`] (#create-a-new-instance) в качестве аргумента с именем `$sessionResyncInterval`.

#### Checking roles / Проверка ролей

```php
try {
    if ($auth->admin()->doesUserHaveRole($userId, \Mnv\Auth\Role::ADMIN)) {
        echo 'The specified user is an administrator';
    }
    else {
        echo 'The specified user is not an administrator';
    }
}
catch (\Mnv\Auth\UnknownIdException $e) {
    die('Unknown user ID');
}
```

В качестве альтернативы вы можете получить список всех ролей, которые были назначены пользователю:

```php
$auth->admin()->getRolesForUserById($userId);
```

#### Impersonating users (logging in as user) 
#### Выдача себя за пользователей (вход в систему как пользователь)

```php
try {
    $auth->admin()->logInAsUserById($_POST['id']);
}
catch (\Mnv\Auth\UnknownIdException $e) {
    die('Unknown ID');
}
catch (\Mnv\Auth\EmailNotVerifiedException $e) {
    die('Email address not verified');
}

// or

try {
    $auth->admin()->logInAsUserByEmail($_POST['email']);
}
catch (\Mnv\Auth\InvalidEmailException $e) {
    die('Unknown email address');
}
catch (\Mnv\Auth\EmailNotVerifiedException $e) {
    die('Email address not verified');
}

// or

try {
    $auth->admin()->logInAsUserByUsername($_POST['username']);
}
catch (\Mnv\Auth\UnknownUsernameException $e) {
    die('Unknown username');
}
catch (\Mnv\Auth\AmbiguousUsernameException $e) {
    die('Ambiguous username');
}
catch (\Mnv\Auth\EmailNotVerifiedException $e) {
    die('Email address not verified');
}
```

#### Changing a user’s password 
#### Смена пароля пользователя

```php
try {
    $auth->admin()->changePasswordForUserById($_POST['id'], $_POST['newPassword']);
}
catch (\Mnv\Auth\UnknownIdException $e) {
    die('Unknown ID');
}
catch (\Mnv\Auth\InvalidPasswordException $e) {
    die('Invalid password');
}

// or

try {
    $auth->admin()->changePasswordForUserByUsername($_POST['username'], $_POST['newPassword']);
}
catch (\Mnv\Auth\UnknownUsernameException $e) {
    die('Unknown username');
}
catch (\Mnv\Auth\AmbiguousUsernameException $e) {
    die('Ambiguous username');
}
catch (\Mnv\Auth\InvalidPasswordException $e) {
    die('Invalid password');
}
```

### Cookies

Эта библиотека использует два файла cookie для сохранения состояния на клиенте: первый, имя которого вы можете получить, используя

```php
\session_name();
```

это общий (обязательный) файл cookie сеанса. Второй (необязательный) файл cookie используется только для [постоянных входов в систему] (#keep-the-user-logged-in), и его имя можно получить следующим образом:

```php
\Mnv\Auth\Auth::createRememberCookieName();
```

#### Renaming the library’s cookies

Вы можете переименовать файл cookie сеанса, используемый этой библиотекой, одним из следующих способов в порядке рекомендации:


 * В [конфигурации PHP] (https://www.php.net/manual/configuration.file.php) (`php.ini`) найдите строку с директивой` session.name` и измените ее значение на что-нибудь как `session_v1`, например:

   ```
   session.name = session_v1
   ```

 * Как можно раньше в вашем приложении и до создания экземпляра `Auth` вызовите `\ini_set`, чтобы изменить `session.name` на что-то вроде` session_v1`, как в:

   ```php
   \ini_set('session.name', 'session_v1');
   ```

   Чтобы это работало, для параметра session.auto_start необходимо установить значение 0 в [конфигурации PHP] (https://www.php.net/manual/configuration.file.php) (php.ini).

 * Как можно раньше в вашем приложении и до того, как вы создадите экземпляр `Auth`, вызовите` \ session_name` с аргументом вроде `session_v1`, как в:

   ```php
   \session_name('session_v1');
   ```

Чтобы это работало, для параметра session.auto_start необходимо установить значение 0 в [конфигурации PHP] (https://www.php.net/manual/configuration.file.php) (php.ini).

Имя файла cookie для [постоянных входов в систему] (# keep-the-user-logged-in) также изменится - автоматически - после изменения имени файла cookie сеанса.

#### Определение области домена для файлов cookie

Атрибут «domain» файла cookie определяет, для какого домена (и каких поддоменов) файл cookie будет действителен, и, таким образом, где будет доступен сеанс пользователя и состояние аутентификации.

Рекомендуемое значение по умолчанию - пустая строка, что означает, что файл cookie будет действителен только для * точного * текущего хоста, * исключая * любые поддомены, которые могут существовать. Вы должны использовать другое значение только в том случае, если вам нужно совместно использовать файлы cookie между разными поддоменами. Часто вам нужно совместно использовать файлы cookie между пустым доменом и субдоменом `www`, но вы также можете захотеть поделиться ими между любым другим набором субдоменов.

Какой бы набор субдоменов вы ни выбрали, вы должны установить для атрибута cookie * наиболее конкретное * доменное имя, которое по-прежнему включает все ваши требуемые субдомены. Например, чтобы совместно использовать файлы cookie между example.com и www.example.com, вы должны установить для атрибута значение example.com. Но если вы хотите использовать файлы cookie для `sub1.app.example.com` и` sub2.app.example.com`, вам следует установить для атрибута значение `app.example.com`. Любое явно указанное доменное имя всегда * включает * все поддомены, которые могут существовать.

Вы можете изменить атрибут одним из следующих способов в порядке рекомендации:

 * В [конфигурации PHP] (https://www.php.net/manual/configuration.file.php) (`php.ini`) найдите строку с директивой` session.cookie_domain` и измените ее значение по желанию. , например:

   ```
   session.cookie_domain = example.com
   ```

 * Как можно раньше в вашем приложении и до того, как вы создадите экземпляр Auth, вызовите `\ini_set`, чтобы изменить значение директивы `session.cookie_domain` по желанию, например:

   ```php
   \ini_set('session.cookie_domain', 'example.com');
   ```

   Чтобы это работало, для параметра session.auto_start необходимо установить значение 0 в [конфигурации PHP] (https://www.php.net/manual/configuration.file.php) (php.ini).

#### Ограничение пути, по которому доступны файлы cookie

Атрибут `path` файла `cookie` определяет, для каких каталогов (и подкаталогов) файл `cookie` будет действителен, и, таким образом, где будет доступен сеанс пользователя и состояние аутентификации.

В большинстве случаев вам нужно сделать файлы cookie доступными для всех путей, т. Е. Любого каталога и файла, начиная с корневого каталога. Это то, что делает значение атрибута "/", которое также является рекомендуемым значением по умолчанию. Вам следует только изменить этот атрибут на другое значение, например `/ path / to / subfolder`, если вы хотите ограничить каталоги, в которых ваши файлы cookie будут доступны, например для размещения нескольких приложений бок о бок в разных каталогах под одним и тем же доменным именем.

Вы можете изменить атрибут одним из следующих способов в порядке рекомендации:

 * В [конфигурации PHP] (https://www.php.net/manual/configuration.file.php) (`php.ini`) найдите строку с директивой `session.cookie_path` и измените ее значение по желанию. , например:

   ```
   session.cookie_path = /
   ```

 * Как можно раньше в вашем приложении и до того, как вы создадите экземпляр Auth, вызовите `\ini_set`, чтобы изменить значение директивы `session.cookie_path` по желанию, например:

   ```php
   \ini_set('session.cookie_path', '/');
   ```

   Чтобы это работало, для параметра `session.auto_start` необходимо установить значение 0 в [конфигурации PHP] (https://www.php.net/manual/configuration.file.php) (php.ini).

#### Контроль доступа к файлам cookie на стороне клиента

Используя атрибут `httponly`, вы можете контролировать, должны ли клиентские скрипты, то есть `JavaScript`, иметь доступ к вашим файлам `cookie` или нет. 
По соображениям безопасности лучше всего * запретить * скрипту доступ к вашим файлам `cookie`, что, например, снижает ущерб, который могут нанести успешные XSS-атаки против вашего приложения.

Таким образом, вы всегда должны устанавливать для `httponly` значение `1`, за исключением тех редких случаев, когда вам действительно нужен доступ к файлам `cookie` из `JavaScript`, и вы не можете найти лучшего решения. 
В таких случаях установите для атрибута значение `0`, но помните о последствиях.

Вы можете изменить атрибут одним из следующих способов в порядке рекомендации:

 * В [конфигурации PHP] (https://www.php.net/manual/configuration.file.php) (`php.ini`) найдите строку с директивой` session.cookie_httponly` и измените ее значение по желанию. , например:

   ```
   session.cookie_httponly = 1
   ```

 * Как можно раньше в вашем приложении и до того, как вы создадите экземпляр Auth, вызовите `\ini_set`, чтобы изменить значение директивы `session.cookie_httponly` по желанию, например:

   ```php
   \ini_set('session.cookie_httponly', 1);
   ```

   Чтобы это работало, для параметра `session.auto_start` необходимо установить значение 0 в [конфигурации PHP] (https://www.php.net/manual/configuration.file.php) (php.ini).

#### Настройка безопасности транспорта для файлов cookie

Используя атрибут `secure`, вы можете указать, следует ли отправлять файлы `cookie` через * любое * соединение, включая обычный `HTTP`, или требуется ли безопасное соединение, например `HTTPS (с SSL / TLS)`. 
Первый (менее безопасный) режим можно выбрать, установив для атрибута значение `0`, а второй (более безопасный) режим можно выбрать, установив для атрибута значение `1`.

Очевидно, это зависит исключительно от того, можете ли вы обслуживать * все * страницы исключительно через `HTTPS`. 
Если вы можете, вы должны установить для атрибута значение «1» и, возможно, объединить его с перенаправлениями `HTTP` на безопасный протокол и` HTTP Strict Transport Security (HSTS)`. 
В противном случае вам, возможно, придется оставить для атрибута значение «0».

Вы можете изменить атрибут одним из следующих способов в порядке рекомендации:

 * In the [PHP configuration](https://www.php.net/manual/configuration.file.php) (`php.ini`), find the line with the `session.cookie_secure` directive and change its value as desired, e.g.:

   ```
   session.cookie_secure = 1
   ```

 * Как можно раньше в вашем приложении и до того, как вы создадите экземпляр `Auth`, вызовите `\ini_set`, чтобы изменить значение директивы `session.cookie_secure` по желанию, например:

   ```php
   \ini_set('session.cookie_secure', 1);
   ```

   Чтобы это работало, для параметра `session.auto_start` необходимо установить значение 0 в [конфигурации PHP] (https://www.php.net/manual/configuration.file.php) (php.ini).

### Utilities

#### Creating a random string / Создание случайной строки

```php
$length = 24;
$randomStr = \Mnv\Auth\Auth::createRandomString($length);
```

#### Creating a UUID v4 as per RFC 4122 / Создание UUID v4 согласно RFC 4122

```php
$uuid = \Mnv\Auth\Auth::createUuid();
```

### Reading and writing session data / Чтение и запись данных сеанса

Для получения подробной информации о том, как удобно читать и записывать данные сеанса, обратитесь к [документации библиотеки сеансов] (https://github.com/nikolaimv/PHP-Cookie#reading-and-writing-session-data), который включен по умолчанию.

## Frequently asked questions / Часто задаваемые вопросы

### What about password hashing? / А как насчет хеширования паролей?

Любой пароль или токен аутентификации автоматически хешируется с помощью функции [«bcrypt»] (https://en.wikipedia.org/wiki/Bcrypt), которая основана на [шифре «Blowfish»] (https: // en. wikipedia.org/wiki/Blowfish_(cipher)) и (по-прежнему) считается одной из самых надежных хэш-функций паролей на сегодняшний день. «Bcrypt» используется с 1024 итерациями, то есть с коэффициентом «стоимости», равным 10. Случайный [«соль»] (https://en.wikipedia.org/wiki/Salt_ (криптография)) также применяется автоматически.

Вы можете проверить эту конфигурацию, посмотрев на хэши в вашей таблице базы данных «users». Если вышеуказанное верно для вашей настройки, все хэши паролей в вашей таблице `users` должны начинаться с префикса `$2$10$`, `$2a$10$` or `$2y$10$`.

Когда в будущем могут появиться новые алгоритмы (такие как [Argon2] (https://en.wikipedia.org/wiki/Argon2)), эта библиотека будет автоматически «обновлять» существующие хэши паролей всякий раз, когда пользователь подписывает в или меняет свой пароль.

### How can I implement custom password requirements? / Как я могу реализовать индивидуальные требования к паролю?

Установление минимальной длины паролей обычно является хорошей идеей. Кроме того, вы можете проверить, находится ли потенциальный пароль в каком-то черном списке, которым вы могли бы управлять в базе данных или в файле, чтобы предотвратить использование словарных слов или часто используемых паролей в вашем приложении.

Чтобы обеспечить максимальную гибкость и простоту использования, эта библиотека была спроектирована таким образом, что она * не * сама * содержит какие-либо дополнительные проверки требований к паролю, а вместо этого позволяет вам обернуть ваши собственные проверки вокруг соответствующих вызовов методов библиотеки. Пример:

```php
function isPasswordAllowed($password) {
    if (\strlen($password) < 8) {
        return false;
    }

    $blacklist = [ 'password1', '123456', 'qwerty' ];

    if (\in_array($password, $blacklist)) {
        return false;
    }

    return true;
}

if (isPasswordAllowed($password)) {
    $auth->register($email, $password);
}
```

### Why are there problems when using other libraries that work with sessions?
### Почему возникают проблемы при использовании других библиотек, которые работают с сессиями?

Вы можете попробовать сначала загрузить эту библиотеку и сначала создать экземпляр `Auth`, * перед * загрузкой других библиотек. 
Кроме того, мы, наверное, мало что можем здесь сделать.

### Why are other sites not able to frame or embed my site?
### Почему другие сайты не могут использовать мой сайт во фреймах или встраивании?

Если вы хотите, чтобы другие пользователи включали ваш сайт в элементы `<frame>`, `<iframe>`, `<object>`, `<embed>` или `<applet>`, вы должны отключить защиту от кликджекинга по умолчанию. :

```php
\header_remove('X-Frame-Options');
```

## Exceptions / Исключения

Эта библиотека выдает два типа исключений, чтобы указать на проблемы:

   * `AuthException` и его подклассы генерируются всякий раз, когда метод не завершается успешно. Вы должны * всегда * перехватывать эти исключения, поскольку они несут в себе обычные ответы на ошибки, на которые вы должны реагировать.
   * `AuthError` и его подклассы выдаются всякий раз, когда возникает внутренняя проблема или библиотека не установлена правильно. Вы не должны * ловить эти исключения.

## General advice / Общий совет

   * Обслуживать * все * страницы только через `HTTPS`, то есть использовать `SSL/TLS` для каждого отдельного запроса.
   * Вы должны установить минимальную длину паролей, например 10 символов, но * никогда * любой максимальной длины, по крайней мере, не менее 100 символов. Более того, вы не должны * ограничивать набор разрешенных символов.
   * Каждый раз, когда пользователя запоминали с помощью функции «запомнить меня», включенной или отключенной во время входа в систему, что означает, что он не вошел в систему, введя свой пароль, вам следует потребовать повторной аутентификации для критических функций.
   * Поощряйте пользователей использовать парольные * фразы *, то есть комбинации слов или даже полные предложения, вместо одинарных * слов *.
   * Не препятствовать правильной работе менеджеров паролей пользователей. Таким образом, используйте только стандартные поля формы и не препятствуйте копированию и вставке.
   * Перед выполнением конфиденциальных операций с учетной записью (например, изменение адреса электронной почты пользователя, удаление учетной записи пользователя) вы всегда должны требовать повторной аутентификации, то есть требовать от пользователя еще раз подтвердить свои учетные данные.
   * Вы не должны предлагать онлайн-функцию сброса пароля («забытый пароль») для приложений с высоким уровнем безопасности.
   * Для приложений с высоким уровнем безопасности не следует использовать адреса электронной почты в качестве идентификаторов. Вместо этого выберите идентификаторы, специфичные для приложения и секрета, например внутренний номер клиента.

## License

This project is licensed under the terms of the [MIT License](https://opensource.org/licenses/MIT).
